Use dep to vendor things

This commit is contained in:
2018-03-18 04:23:13 +00:00
parent d572a19352
commit 1ba64f2aa8
123 changed files with 34229 additions and 0 deletions

21
vendor/github.com/faiface/glhf/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Michal Štrba
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

191
vendor/github.com/faiface/glhf/README.md generated vendored Normal file
View File

@@ -0,0 +1,191 @@
# glhf [![GoDoc](https://godoc.org/github.com/faiface/glhf?status.svg)](http://godoc.org/github.com/faiface/glhf) [![Report card](https://goreportcard.com/badge/github.com/faiface/glhf)](https://goreportcard.com/report/github.com/faiface/glhf)
open**GL** **H**ave **F**un - A Go package that makes life with OpenGL enjoyable.
```
go get github.com/faiface/glhf
```
## Main features
- Garbage collected OpenGL objects
- Dynamically sized vertex slices (vertex arrays are boring)
- Textures, Shaders, Frames (reasonably managed framebuffers)
- Always possible to use standard OpenGL with `glhf`
## Motivation
OpenGL is verbose, it's usage patterns are repetitive and it's manual memory management doesn't fit
Go's design. When making a game development library, it's usually desirable to create some
higher-level abstractions around OpenGL. This library is a take on that.
## Contribute!
The library is young and many features are still missing. If you find a bug, have a proposal or a
feature request, _do an issue_!. If you know how to implement something that's missing, _do a pull
request_.
## Code
The following are parts of the demo program, which can be found in the [examples](https://github.com/faiface/glhf/tree/master/examples/demo).
```go
// ... GLFW window creation and stuff ...
// vertex shader source
var vertexShader = `
#version 330 core
in vec2 position;
in vec2 texture;
out vec2 Texture;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
Texture = texture;
}
`
// fragment shader source
var fragmentShader = `
#version 330 core
in vec2 Texture;
out vec4 color;
uniform sampler2D tex;
void main() {
color = texture(tex, Texture);
}
`
var (
// Here we define a vertex format of our vertex slice. It's actually a basic slice
// literal.
//
// The vertex format consists of names and types of the attributes. The name is the
// name that the attribute is referenced by inside a shader.
vertexFormat = glhf.AttrFormat{
{Name: "position", Type: glhf.Vec2},
{Name: "texture", Type: glhf.Vec2},
}
// Here we declare some variables for later use.
shader *glhf.Shader
texture *glhf.Texture
slice *glhf.VertexSlice
)
// Here we load an image from a file. The loadImage function is not within the library, it
// just loads and returns a image.NRGBA.
gopherImage, err := loadImage("celebrate.png")
if err != nil {
panic(err)
}
// Every OpenGL call needs to be done inside the main thread.
mainthread.Call(func() {
var err error
// Here we create a shader. The second argument is the format of the uniform
// attributes. Since our shader has no uniform attributes, the format is empty.
shader, err = glhf.NewShader(vertexFormat, glhf.AttrFormat{}, vertexShader, fragmentShader)
// If the shader compilation did not go successfully, an error with a full
// description is returned.
if err != nil {
panic(err)
}
// We create a texture from the loaded image.
texture = glhf.NewTexture(
gopherImage.Bounds().Dx(),
gopherImage.Bounds().Dy(),
true,
gopherImage.Pix,
)
// And finally, we make a vertex slice, which is basically a dynamically sized
// vertex array. The length of the slice is 6 and the capacity is the same.
//
// The slice inherits the vertex format of the supplied shader. Also, it should
// only be used with that shader.
slice = glhf.MakeVertexSlice(shader, 6, 6)
// Before we use a slice, we need to Begin it. The same holds for all objects in
// GLHF.
slice.Begin()
// We assign data to the vertex slice. The values are in the order as in the vertex
// format of the slice (shader). Each two floats correspond to an attribute of type
// glhf.Vec2.
slice.SetVertexData([]float32{
-1, -1, 0, 1,
+1, -1, 1, 1,
+1, +1, 1, 0,
-1, -1, 0, 1,
+1, +1, 1, 0,
-1, +1, 0, 0,
})
// When we're done with the slice, we End it.
slice.End()
})
shouldQuit := false
for !shouldQuit {
mainthread.Call(func() {
// ... GLFW stuff ...
// Clear the window.
glhf.Clear(1, 1, 1, 1)
// Here we Begin/End all necessary objects and finally draw the vertex
// slice.
shader.Begin()
texture.Begin()
slice.Begin()
slice.Draw()
slice.End()
texture.End()
shader.End()
// ... GLFW stuff ...
})
}
```
## FAQ
### Which version of OpenGL does GLHF use?
It uses OpenGL 3.3 and uses
[`github.com/go-gl/gl/v3.3-core/gl`](https://github.com/go-gl/gl/tree/master/v3.3-core/gl).
### Why do I have to use `github.com/faiface/mainthread` package with GLHF?
First of all, OpenGL has to be done from one thread and many operating systems require, that the one
thread will be the main thread of your application.
But why that specific package? GLHF uses the `mainthread` package to do the garbage collection of
OpenGL objects, which is super convenient. So in order for it to work correctly, you have to
initialize the `mainthread` package through `mainthread.Run`. However, once you call this function
there is no way to run functions on the main thread, except for through the `mainthread` package.
### Why is the important XY feature not included?
I probably didn't need it yet. If you want that features, create an issue or implement it and do a
pull request.
### Does GLHF create windows for me?
No. You have to use another library for windowing, e.g.
[github.com/go-gl/glfw/v3.2/glfw](https://github.com/go-gl/glfw/tree/master/v3.2/glfw).
### Why no tests?
If you find a way to automatically test OpenGL, I may add tests.

80
vendor/github.com/faiface/glhf/attr.go generated vendored Normal file
View File

@@ -0,0 +1,80 @@
package glhf
// AttrFormat defines names and types of OpenGL attributes (vertex format, uniform format, etc.).
//
// Example:
// AttrFormat{{"position", Vec2}, {"color", Vec4}, {"texCoord": Vec2}}
type AttrFormat []Attr
// Size returns the total size of all attributes of the AttrFormat.
func (af AttrFormat) Size() int {
total := 0
for _, attr := range af {
total += attr.Type.Size()
}
return total
}
// Attr represents an arbitrary OpenGL attribute, such as a vertex attribute or a shader
// uniform attribute.
type Attr struct {
Name string
Type AttrType
}
// AttrType represents the type of an OpenGL attribute.
type AttrType int
// List of all possible attribute types.
const (
Int AttrType = iota
Float
Vec2
Vec3
Vec4
Mat2
Mat23
Mat24
Mat3
Mat32
Mat34
Mat4
Mat42
Mat43
)
// Size returns the size of a type in bytes.
func (at AttrType) Size() int {
switch at {
case Int:
return 4
case Float:
return 4
case Vec2:
return 2 * 4
case Vec3:
return 3 * 4
case Vec4:
return 4 * 4
case Mat2:
return 2 * 2 * 4
case Mat23:
return 2 * 3 * 4
case Mat24:
return 2 * 4 * 4
case Mat3:
return 3 * 3 * 4
case Mat32:
return 3 * 2 * 4
case Mat34:
return 3 * 4 * 4
case Mat4:
return 4 * 4 * 4
case Mat42:
return 4 * 2 * 4
case Mat43:
return 4 * 3 * 4
default:
panic("size of vertex attribute type: invalid type")
}
}

7
vendor/github.com/faiface/glhf/doc.go generated vendored Normal file
View File

@@ -0,0 +1,7 @@
// Package glhf provides abstractions around the basic OpenGL primitives and operations.
//
// All calls should be done from the main thread using "github.com/faiface/mainthread" package.
//
// This package deliberately does not handle nor report trivial OpenGL errors, it's up to you to
// cause none. It does of course report errors like shader compilation error and such.
package glhf

108
vendor/github.com/faiface/glhf/frame.go generated vendored Normal file
View File

@@ -0,0 +1,108 @@
package glhf
import (
"runtime"
"github.com/faiface/mainthread"
"github.com/go-gl/gl/v3.3-core/gl"
)
// Frame is a fixed resolution texture that you can draw on.
type Frame struct {
fb, rf, df binder // framebuffer, read framebuffer, draw framebuffer
tex *Texture
}
// NewFrame creates a new fully transparent Frame with given dimensions in pixels.
func NewFrame(width, height int, smooth bool) *Frame {
f := &Frame{
fb: binder{
restoreLoc: gl.FRAMEBUFFER_BINDING,
bindFunc: func(obj uint32) {
gl.BindFramebuffer(gl.FRAMEBUFFER, obj)
},
},
rf: binder{
restoreLoc: gl.READ_FRAMEBUFFER_BINDING,
bindFunc: func(obj uint32) {
gl.BindFramebuffer(gl.READ_FRAMEBUFFER, obj)
},
},
df: binder{
restoreLoc: gl.DRAW_FRAMEBUFFER_BINDING,
bindFunc: func(obj uint32) {
gl.BindFramebuffer(gl.DRAW_FRAMEBUFFER, obj)
},
},
tex: NewTexture(width, height, smooth, make([]uint8, width*height*4)),
}
gl.GenFramebuffers(1, &f.fb.obj)
f.fb.bind()
gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, f.tex.tex.obj, 0)
f.fb.restore()
runtime.SetFinalizer(f, (*Frame).delete)
return f
}
func (f *Frame) delete() {
mainthread.CallNonBlock(func() {
gl.DeleteFramebuffers(1, &f.fb.obj)
})
}
// ID returns the OpenGL framebuffer ID of this Frame.
func (f *Frame) ID() uint32 {
return f.fb.obj
}
// Begin binds the Frame. All draw operations will target this Frame until End is called.
func (f *Frame) Begin() {
f.fb.bind()
}
// End unbinds the Frame. All draw operations will go to whatever was bound before this Frame.
func (f *Frame) End() {
f.fb.restore()
}
// Blit copies rectangle (sx0, sy0, sx1, sy1) in this Frame onto rectangle (dx0, dy0, dx1, dy1) in
// dst Frame.
//
// If the dst Frame is nil, the destination will be the framebuffer 0, which is the screen.
//
// If the sizes of the rectangles don't match, the source will be stretched to fit the destination
// rectangle. The stretch will be either smooth or pixely according to the source Frame's
// smoothness.
func (f *Frame) Blit(dst *Frame, sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1 int) {
f.rf.obj = f.fb.obj
if dst != nil {
f.df.obj = dst.fb.obj
} else {
f.df.obj = 0
}
f.rf.bind()
f.df.bind()
filter := gl.NEAREST
if f.tex.smooth {
filter = gl.LINEAR
}
gl.BlitFramebuffer(
int32(sx0), int32(sy0), int32(sx1), int32(sy1),
int32(dx0), int32(dy0), int32(dx1), int32(dy1),
gl.COLOR_BUFFER_BIT, uint32(filter),
)
f.rf.restore()
f.df.restore()
}
// Texture returns the Frame's underlying Texture that the Frame draws on.
func (f *Frame) Texture() *Texture {
return f.tex
}

11
vendor/github.com/faiface/glhf/interface.go generated vendored Normal file
View File

@@ -0,0 +1,11 @@
package glhf
// BeginEnder is an interface for manipulating OpenGL state.
//
// OpenGL is a state machine. Every object can 'enter' it's state and 'leave' it's state. For
// example, you can bind a buffer and unbind a buffer, bind a texture and unbind it, use shader
// and unuse it, and so on.
type BeginEnder interface {
Begin()
End()
}

51
vendor/github.com/faiface/glhf/orphan.go generated vendored Normal file
View File

@@ -0,0 +1,51 @@
package glhf
import "github.com/go-gl/gl/v3.3-core/gl"
// Init initializes OpenGL by loading function pointers from the active OpenGL context.
// This function must be manually run inside the main thread (using "github.com/faiface/mainthread"
// package).
//
// It must be called under the presence of an active OpenGL context, e.g., always after calling
// window.MakeContextCurrent(). Also, always call this function when switching contexts.
func Init() {
err := gl.Init()
if err != nil {
panic(err)
}
gl.Enable(gl.BLEND)
gl.Enable(gl.SCISSOR_TEST)
gl.BlendEquation(gl.FUNC_ADD)
}
// Clear clears the current framebuffer or window with the given color.
func Clear(r, g, b, a float32) {
gl.ClearColor(r, g, b, a)
gl.Clear(gl.COLOR_BUFFER_BIT)
}
// Bounds sets the drawing bounds in pixels. Drawing outside bounds is always discarted.
//
// Calling this function is equivalent to setting viewport and scissor in OpenGL.
func Bounds(x, y, w, h int) {
gl.Viewport(int32(x), int32(y), int32(w), int32(h))
gl.Scissor(int32(x), int32(y), int32(w), int32(h))
}
// BlendFactor represents a source or destination blend factor.
type BlendFactor int
// Here's the list of all blend factors.
const (
One = BlendFactor(gl.ONE)
Zero = BlendFactor(gl.ZERO)
SrcAlpha = BlendFactor(gl.SRC_ALPHA)
DstAlpha = BlendFactor(gl.DST_ALPHA)
OneMinusSrcAlpha = BlendFactor(gl.ONE_MINUS_SRC_ALPHA)
OneMinusDstAlpha = BlendFactor(gl.ONE_MINUS_DST_ALPHA)
)
// BlendFunc sets the source and destination blend factor.
func BlendFunc(src, dst BlendFactor) {
gl.BlendFunc(uint32(src), uint32(dst))
}

224
vendor/github.com/faiface/glhf/shader.go generated vendored Normal file
View File

@@ -0,0 +1,224 @@
package glhf
import (
"fmt"
"runtime"
"github.com/faiface/mainthread"
"github.com/go-gl/gl/v3.3-core/gl"
"github.com/go-gl/mathgl/mgl32"
)
// Shader is an OpenGL shader program.
type Shader struct {
program binder
vertexFmt AttrFormat
uniformFmt AttrFormat
uniformLoc []int32
}
// NewShader creates a new shader program from the specified vertex shader and fragment shader
// sources.
//
// Note that vertexShader and fragmentShader parameters must contain the source code, they're
// not filenames.
func NewShader(vertexFmt, uniformFmt AttrFormat, vertexShader, fragmentShader string) (*Shader, error) {
shader := &Shader{
program: binder{
restoreLoc: gl.CURRENT_PROGRAM,
bindFunc: func(obj uint32) {
gl.UseProgram(obj)
},
},
vertexFmt: vertexFmt,
uniformFmt: uniformFmt,
uniformLoc: make([]int32, len(uniformFmt)),
}
var vshader, fshader uint32
// vertex shader
{
vshader = gl.CreateShader(gl.VERTEX_SHADER)
src, free := gl.Strs(vertexShader)
defer free()
length := int32(len(vertexShader))
gl.ShaderSource(vshader, 1, src, &length)
gl.CompileShader(vshader)
var success int32
gl.GetShaderiv(vshader, gl.COMPILE_STATUS, &success)
if success == gl.FALSE {
var logLen int32
gl.GetShaderiv(vshader, gl.INFO_LOG_LENGTH, &logLen)
infoLog := make([]byte, logLen)
gl.GetShaderInfoLog(vshader, logLen, nil, &infoLog[0])
return nil, fmt.Errorf("error compiling vertex shader: %s", string(infoLog))
}
defer gl.DeleteShader(vshader)
}
// fragment shader
{
fshader = gl.CreateShader(gl.FRAGMENT_SHADER)
src, free := gl.Strs(fragmentShader)
defer free()
length := int32(len(fragmentShader))
gl.ShaderSource(fshader, 1, src, &length)
gl.CompileShader(fshader)
var success int32
gl.GetShaderiv(fshader, gl.COMPILE_STATUS, &success)
if success == gl.FALSE {
var logLen int32
gl.GetShaderiv(fshader, gl.INFO_LOG_LENGTH, &logLen)
infoLog := make([]byte, logLen)
gl.GetShaderInfoLog(fshader, logLen, nil, &infoLog[0])
return nil, fmt.Errorf("error compiling fragment shader: %s", string(infoLog))
}
defer gl.DeleteShader(fshader)
}
// shader program
{
shader.program.obj = gl.CreateProgram()
gl.AttachShader(shader.program.obj, vshader)
gl.AttachShader(shader.program.obj, fshader)
gl.LinkProgram(shader.program.obj)
var success int32
gl.GetProgramiv(shader.program.obj, gl.LINK_STATUS, &success)
if success == gl.FALSE {
var logLen int32
gl.GetProgramiv(shader.program.obj, gl.INFO_LOG_LENGTH, &logLen)
infoLog := make([]byte, logLen)
gl.GetProgramInfoLog(shader.program.obj, logLen, nil, &infoLog[0])
return nil, fmt.Errorf("error linking shader program: %s", string(infoLog))
}
}
// uniforms
for i, uniform := range uniformFmt {
loc := gl.GetUniformLocation(shader.program.obj, gl.Str(uniform.Name+"\x00"))
shader.uniformLoc[i] = loc
}
runtime.SetFinalizer(shader, (*Shader).delete)
return shader, nil
}
func (s *Shader) delete() {
mainthread.CallNonBlock(func() {
gl.DeleteProgram(s.program.obj)
})
}
// ID returns the OpenGL ID of this Shader.
func (s *Shader) ID() uint32 {
return s.program.obj
}
// VertexFormat returns the vertex attribute format of this Shader. Do not change it.
func (s *Shader) VertexFormat() AttrFormat {
return s.vertexFmt
}
// UniformFormat returns the uniform attribute format of this Shader. Do not change it.
func (s *Shader) UniformFormat() AttrFormat {
return s.uniformFmt
}
// SetUniformAttr sets the value of a uniform attribute of this Shader. The attribute is
// specified by the index in the Shader's uniform format.
//
// If the uniform attribute does not exist in the Shader, this method returns false.
//
// Supplied value must correspond to the type of the attribute. Correct types are these
// (right-hand is the type of the value):
// Attr{Type: Int}: int32
// Attr{Type: Float}: float32
// Attr{Type: Vec2}: mgl32.Vec2
// Attr{Type: Vec3}: mgl32.Vec3
// Attr{Type: Vec4}: mgl32.Vec4
// Attr{Type: Mat2}: mgl32.Mat2
// Attr{Type: Mat23}: mgl32.Mat2x3
// Attr{Type: Mat24}: mgl32.Mat2x4
// Attr{Type: Mat3}: mgl32.Mat3
// Attr{Type: Mat32}: mgl32.Mat3x2
// Attr{Type: Mat34}: mgl32.Mat3x4
// Attr{Type: Mat4}: mgl32.Mat4
// Attr{Type: Mat42}: mgl32.Mat4x2
// Attr{Type: Mat43}: mgl32.Mat4x3
// No other types are supported.
//
// The Shader must be bound before calling this method.
func (s *Shader) SetUniformAttr(uniform int, value interface{}) (ok bool) {
if s.uniformLoc[uniform] < 0 {
return false
}
switch s.uniformFmt[uniform].Type {
case Int:
value := value.(int32)
gl.Uniform1iv(s.uniformLoc[uniform], 1, &value)
case Float:
value := value.(float32)
gl.Uniform1fv(s.uniformLoc[uniform], 1, &value)
case Vec2:
value := value.(mgl32.Vec2)
gl.Uniform2fv(s.uniformLoc[uniform], 1, &value[0])
case Vec3:
value := value.(mgl32.Vec3)
gl.Uniform3fv(s.uniformLoc[uniform], 1, &value[0])
case Vec4:
value := value.(mgl32.Vec4)
gl.Uniform4fv(s.uniformLoc[uniform], 1, &value[0])
case Mat2:
value := value.(mgl32.Mat2)
gl.UniformMatrix2fv(s.uniformLoc[uniform], 1, false, &value[0])
case Mat23:
value := value.(mgl32.Mat2x3)
gl.UniformMatrix2x3fv(s.uniformLoc[uniform], 1, false, &value[0])
case Mat24:
value := value.(mgl32.Mat2x4)
gl.UniformMatrix2x4fv(s.uniformLoc[uniform], 1, false, &value[0])
case Mat3:
value := value.(mgl32.Mat3)
gl.UniformMatrix3fv(s.uniformLoc[uniform], 1, false, &value[0])
case Mat32:
value := value.(mgl32.Mat3x2)
gl.UniformMatrix3x2fv(s.uniformLoc[uniform], 1, false, &value[0])
case Mat34:
value := value.(mgl32.Mat3x4)
gl.UniformMatrix3x4fv(s.uniformLoc[uniform], 1, false, &value[0])
case Mat4:
value := value.(mgl32.Mat4)
gl.UniformMatrix4fv(s.uniformLoc[uniform], 1, false, &value[0])
case Mat42:
value := value.(mgl32.Mat4x2)
gl.UniformMatrix4x2fv(s.uniformLoc[uniform], 1, false, &value[0])
case Mat43:
value := value.(mgl32.Mat4x3)
gl.UniformMatrix4x3fv(s.uniformLoc[uniform], 1, false, &value[0])
default:
panic("set uniform attr: invalid attribute type")
}
return true
}
// Begin binds the Shader program. This is necessary before using the Shader.
func (s *Shader) Begin() {
s.program.bind()
}
// End unbinds the Shader program and restores the previous one.
func (s *Shader) End() {
s.program.restore()
}

148
vendor/github.com/faiface/glhf/texture.go generated vendored Normal file
View File

@@ -0,0 +1,148 @@
package glhf
import (
"runtime"
"github.com/faiface/mainthread"
"github.com/go-gl/gl/v3.3-core/gl"
"github.com/go-gl/mathgl/mgl32"
)
// Texture is an OpenGL texture.
type Texture struct {
tex binder
width, height int
smooth bool
}
// NewTexture creates a new texture with the specified width and height with some initial
// pixel values. The pixels must be a sequence of RGBA values (one byte per component).
func NewTexture(width, height int, smooth bool, pixels []uint8) *Texture {
tex := &Texture{
tex: binder{
restoreLoc: gl.TEXTURE_BINDING_2D,
bindFunc: func(obj uint32) {
gl.BindTexture(gl.TEXTURE_2D, obj)
},
},
width: width,
height: height,
}
gl.GenTextures(1, &tex.tex.obj)
tex.Begin()
defer tex.End()
// initial data
gl.TexImage2D(
gl.TEXTURE_2D,
0,
gl.RGBA,
int32(width),
int32(height),
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
gl.Ptr(pixels),
)
borderColor := mgl32.Vec4{0, 0, 0, 0}
gl.TexParameterfv(gl.TEXTURE_2D, gl.TEXTURE_BORDER_COLOR, &borderColor[0])
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_BORDER)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_BORDER)
tex.SetSmooth(smooth)
runtime.SetFinalizer(tex, (*Texture).delete)
return tex
}
func (t *Texture) delete() {
mainthread.CallNonBlock(func() {
gl.DeleteTextures(1, &t.tex.obj)
})
}
// ID returns the OpenGL ID of this Texture.
func (t *Texture) ID() uint32 {
return t.tex.obj
}
// Width returns the width of the Texture in pixels.
func (t *Texture) Width() int {
return t.width
}
// Height returns the height of the Texture in pixels.
func (t *Texture) Height() int {
return t.height
}
// SetPixels sets the content of a sub-region of the Texture. Pixels must be an RGBA byte sequence.
func (t *Texture) SetPixels(x, y, w, h int, pixels []uint8) {
if len(pixels) != w*h*4 {
panic("set pixels: wrong number of pixels")
}
gl.TexSubImage2D(
gl.TEXTURE_2D,
0,
int32(x),
int32(y),
int32(w),
int32(h),
gl.RGBA,
gl.UNSIGNED_BYTE,
gl.Ptr(pixels),
)
}
// Pixels returns the content of a sub-region of the Texture as an RGBA byte sequence.
func (t *Texture) Pixels(x, y, w, h int) []uint8 {
pixels := make([]uint8, t.width*t.height*4)
gl.GetTexImage(
gl.TEXTURE_2D,
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
gl.Ptr(pixels),
)
subPixels := make([]uint8, w*h*4)
for i := 0; i < h; i++ {
row := pixels[(i+y)*t.width*4+x*4 : (i+y)*t.width*4+(x+w)*4]
subRow := subPixels[i*w*4 : (i+1)*w*4]
copy(subRow, row)
}
return subPixels
}
// SetSmooth sets whether the Texture should be drawn "smoothly" or "pixely".
//
// It affects how the Texture is drawn when zoomed. Smooth interpolates between the neighbour
// pixels, while pixely always chooses the nearest pixel.
func (t *Texture) SetSmooth(smooth bool) {
t.smooth = smooth
if smooth {
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
} else {
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
}
}
// Smooth returns whether the Texture is set to be drawn "smooth" or "pixely".
func (t *Texture) Smooth() bool {
return t.smooth
}
// Begin binds the Texture. This is necessary before using the Texture.
func (t *Texture) Begin() {
t.tex.bind()
}
// End unbinds the Texture and restores the previous one.
func (t *Texture) End() {
t.tex.restore()
}

31
vendor/github.com/faiface/glhf/util.go generated vendored Normal file
View File

@@ -0,0 +1,31 @@
package glhf
import "github.com/go-gl/gl/v3.3-core/gl"
type binder struct {
restoreLoc uint32
bindFunc func(uint32)
obj uint32
prev []uint32
}
func (b *binder) bind() *binder {
var prev int32
gl.GetIntegerv(b.restoreLoc, &prev)
b.prev = append(b.prev, uint32(prev))
if b.prev[len(b.prev)-1] != b.obj {
b.bindFunc(b.obj)
}
return b
}
func (b *binder) restore() *binder {
if b.prev[len(b.prev)-1] != b.obj {
b.bindFunc(b.prev[len(b.prev)-1])
}
b.prev = b.prev[:len(b.prev)-1]
return b
}

285
vendor/github.com/faiface/glhf/vertex.go generated vendored Normal file
View File

@@ -0,0 +1,285 @@
package glhf
import (
"fmt"
"runtime"
"github.com/faiface/mainthread"
"github.com/go-gl/gl/v3.3-core/gl"
"github.com/pkg/errors"
)
// VertexSlice points to a portion of (or possibly whole) vertex array. It is used as a pointer,
// contrary to Go's builtin slices. This is, so that append can be 'in-place'. That's for the good,
// because Begin/End-ing a VertexSlice would become super confusing, if append returned a new
// VertexSlice.
//
// It also implements all basic slice-like operations: appending, sub-slicing, etc.
//
// Note that you need to Begin a VertexSlice before getting or updating it's elements or drawing it.
// After you're done with it, you need to End it.
type VertexSlice struct {
va *vertexArray
i, j int
}
// MakeVertexSlice allocates a new vertex array with specified capacity and returns a VertexSlice
// that points to it's first len elements.
//
// Note, that a vertex array is specialized for a specific shader and can't be used with another
// shader.
func MakeVertexSlice(shader *Shader, len, cap int) *VertexSlice {
if len > cap {
panic("failed to make vertex slice: len > cap")
}
return &VertexSlice{
va: newVertexArray(shader, cap),
i: 0,
j: len,
}
}
// VertexFormat returns the format of vertex attributes inside the underlying vertex array of this
// VertexSlice.
func (vs *VertexSlice) VertexFormat() AttrFormat {
return vs.va.format
}
// Stride returns the number of float32 elements occupied by one vertex.
func (vs *VertexSlice) Stride() int {
return vs.va.stride / 4
}
// Len returns the length of the VertexSlice (number of vertices).
func (vs *VertexSlice) Len() int {
return vs.j - vs.i
}
// Cap returns the capacity of an underlying vertex array.
func (vs *VertexSlice) Cap() int {
return vs.va.cap - vs.i
}
// SetLen resizes the VertexSlice to length len.
func (vs *VertexSlice) SetLen(len int) {
vs.End() // vs must have been Begin-ed before calling this method
*vs = vs.grow(len)
vs.Begin()
}
// grow returns supplied vs with length changed to len. Allocates new underlying vertex array if
// necessary. The original content is preserved.
func (vs VertexSlice) grow(len int) VertexSlice {
if len <= vs.Cap() {
// capacity sufficient
return VertexSlice{
va: vs.va,
i: vs.i,
j: vs.i + len,
}
}
// grow the capacity
newCap := vs.Cap()
if newCap < 1024 {
newCap += newCap
} else {
newCap += newCap / 4
}
if newCap < len {
newCap = len
}
newVs := VertexSlice{
va: newVertexArray(vs.va.shader, newCap),
i: 0,
j: len,
}
// preserve the original content
newVs.Begin()
newVs.Slice(0, vs.Len()).SetVertexData(vs.VertexData())
newVs.End()
return newVs
}
// Slice returns a sub-slice of this VertexSlice covering the range [i, j) (relative to this
// VertexSlice).
//
// Note, that the returned VertexSlice shares an underlying vertex array with the original
// VertexSlice. Modifying the contents of one modifies corresponding contents of the other.
func (vs *VertexSlice) Slice(i, j int) *VertexSlice {
if i < 0 || j < i || j > vs.va.cap {
panic("failed to slice vertex slice: index out of range")
}
return &VertexSlice{
va: vs.va,
i: vs.i + i,
j: vs.i + j,
}
}
// SetVertexData sets the contents of the VertexSlice.
//
// The data is a slice of float32's, where each vertex attribute occupies a certain number of
// elements. Namely, Float occupies 1, Vec2 occupies 2, Vec3 occupies 3 and Vec4 occupies 4. The
// attribues in the data slice must be in the same order as in the vertex format of this Vertex
// Slice.
//
// If the length of vertices does not match the length of the VertexSlice, this methdo panics.
func (vs *VertexSlice) SetVertexData(data []float32) {
if len(data)/vs.Stride() != vs.Len() {
fmt.Println(len(data)/vs.Stride(), vs.Len())
panic("set vertex data: wrong length of vertices")
}
vs.va.setVertexData(vs.i, vs.j, data)
}
// VertexData returns the contents of the VertexSlice.
//
// The data is in the same format as with SetVertexData.
func (vs *VertexSlice) VertexData() []float32 {
return vs.va.vertexData(vs.i, vs.j)
}
// Draw draws the content of the VertexSlice.
func (vs *VertexSlice) Draw() {
vs.va.draw(vs.i, vs.j)
}
// Begin binds the underlying vertex array. Calling this method is necessary before using the VertexSlice.
func (vs *VertexSlice) Begin() {
vs.va.begin()
}
// End unbinds the underlying vertex array. Call this method when you're done with VertexSlice.
func (vs *VertexSlice) End() {
vs.va.end()
}
type vertexArray struct {
vao, vbo binder
cap int
format AttrFormat
stride int
offset []int
shader *Shader
}
const vertexArrayMinCap = 4
func newVertexArray(shader *Shader, cap int) *vertexArray {
if cap < vertexArrayMinCap {
cap = vertexArrayMinCap
}
va := &vertexArray{
vao: binder{
restoreLoc: gl.VERTEX_ARRAY_BINDING,
bindFunc: func(obj uint32) {
gl.BindVertexArray(obj)
},
},
vbo: binder{
restoreLoc: gl.ARRAY_BUFFER_BINDING,
bindFunc: func(obj uint32) {
gl.BindBuffer(gl.ARRAY_BUFFER, obj)
},
},
cap: cap,
format: shader.VertexFormat(),
stride: shader.VertexFormat().Size(),
offset: make([]int, len(shader.VertexFormat())),
shader: shader,
}
offset := 0
for i, attr := range va.format {
switch attr.Type {
case Float, Vec2, Vec3, Vec4:
default:
panic(errors.New("failed to create vertex array: invalid attribute type"))
}
va.offset[i] = offset
offset += attr.Type.Size()
}
gl.GenVertexArrays(1, &va.vao.obj)
va.vao.bind()
gl.GenBuffers(1, &va.vbo.obj)
defer va.vbo.bind().restore()
emptyData := make([]byte, cap*va.stride)
gl.BufferData(gl.ARRAY_BUFFER, len(emptyData), gl.Ptr(emptyData), gl.DYNAMIC_DRAW)
for i, attr := range va.format {
loc := gl.GetAttribLocation(shader.program.obj, gl.Str(attr.Name+"\x00"))
var size int32
switch attr.Type {
case Float:
size = 1
case Vec2:
size = 2
case Vec3:
size = 3
case Vec4:
size = 4
}
gl.VertexAttribPointer(
uint32(loc),
size,
gl.FLOAT,
false,
int32(va.stride),
gl.PtrOffset(va.offset[i]),
)
gl.EnableVertexAttribArray(uint32(loc))
}
va.vao.restore()
runtime.SetFinalizer(va, (*vertexArray).delete)
return va
}
func (va *vertexArray) delete() {
mainthread.CallNonBlock(func() {
gl.DeleteVertexArrays(1, &va.vao.obj)
gl.DeleteBuffers(1, &va.vbo.obj)
})
}
func (va *vertexArray) begin() {
va.vao.bind()
va.vbo.bind()
}
func (va *vertexArray) end() {
va.vbo.restore()
va.vao.restore()
}
func (va *vertexArray) draw(i, j int) {
gl.DrawArrays(gl.TRIANGLES, int32(i), int32(i+j))
}
func (va *vertexArray) setVertexData(i, j int, data []float32) {
if j-i == 0 {
// avoid setting 0 bytes of buffer data
return
}
gl.BufferSubData(gl.ARRAY_BUFFER, i*va.stride, len(data)*4, gl.Ptr(data))
}
func (va *vertexArray) vertexData(i, j int) []float32 {
if j-i == 0 {
// avoid getting 0 bytes of buffer data
return nil
}
data := make([]float32, (j-i)*va.stride/4)
gl.GetBufferSubData(gl.ARRAY_BUFFER, i*va.stride, len(data)*4, gl.Ptr(data))
return data
}

21
vendor/github.com/faiface/mainthread/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Michal Štrba
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

71
vendor/github.com/faiface/mainthread/README.md generated vendored Normal file
View File

@@ -0,0 +1,71 @@
# mainthread [![GoDoc](https://godoc.org/github.com/faiface/mainthread?status.svg)](http://godoc.org/github.com/faiface/mainthread) [![Report card](https://goreportcard.com/badge/github.com/faiface/mainthread)](https://goreportcard.com/report/github.com/faiface/mainthread)
Package mainthread allows you to run code on the main operating system thread.
`go get github.com/faiface/mainthread`
Operating systems often require, that code which deals with windows and graphics has to run on the
main thread. This is however somehow challenging in Go due to Go's concurrent nature.
This package makes it easily possible.
All you need to do is put your main code into a separate function and call `mainthread.Run` from
your real main, like this:
```go
package main
import (
"fmt"
"github.com/faiface/mainthread"
)
func run() {
// now we can run stuff on the main thread like this
mainthread.Call(func() {
fmt.Println("printing from the main thread")
})
fmt.Println("printing from another thread")
}
func main() {
mainthread.Run(run) // enables mainthread package and runs run in a separate goroutine
}
```
## More functions
If you don't wish to wait until a function finishes running on the main thread, use
`mainthread.CallNonBlock`:
```go
mainthread.CallNonBlock(func() {
fmt.Println("i'm in the main thread")
})
fmt.Println("but imma be likely printed first, cuz i don't wait")
```
If you want to get some value returned from the main thread, you can use `mainthread.CallErr` or
`mainthread.CallVal`:
```go
err := mainthread.CallErr(func() error {
return nil // i don't do nothing wrong
})
val := mainthread.CallVal(func() interface{} {
return 42 // the meaning of life, universe and everything
})
```
If `mainthread.CallErr` or `mainthread.CallVal` aren't sufficient for you, you can just assign
variables from within the main thread:
```go
var x, y int
mainthread.Call(func() {
x, y = 1, 2
})
```
However, be careful with `mainthread.CallNonBlock` when dealing with local variables.

87
vendor/github.com/faiface/mainthread/mainthread.go generated vendored Normal file
View File

@@ -0,0 +1,87 @@
package mainthread
import (
"errors"
"runtime"
)
// CallQueueCap is the capacity of the call queue. This means how many calls to CallNonBlock will not
// block until some call finishes.
//
// The default value is 16 and should be good for 99% usecases.
var CallQueueCap = 16
var (
callQueue chan func()
)
func init() {
runtime.LockOSThread()
}
func checkRun() {
if callQueue == nil {
panic(errors.New("mainthread: did not call Run"))
}
}
// Run enables mainthread package functionality. To use mainthread package, put your main function
// code into the run function (the argument to Run) and simply call Run from the real main function.
//
// Run returns when run (argument) function finishes.
func Run(run func()) {
callQueue = make(chan func(), CallQueueCap)
done := make(chan struct{})
go func() {
run()
done <- struct{}{}
}()
for {
select {
case f := <-callQueue:
f()
case <-done:
return
}
}
}
// CallNonBlock queues function f on the main thread and returns immediately. Does not wait until f
// finishes.
func CallNonBlock(f func()) {
checkRun()
callQueue <- f
}
// Call queues function f on the main thread and blocks until the function f finishes.
func Call(f func()) {
checkRun()
done := make(chan struct{})
callQueue <- func() {
f()
done <- struct{}{}
}
<-done
}
// CallErr queues function f on the main thread and returns an error returned by f.
func CallErr(f func() error) error {
checkRun()
errChan := make(chan error)
callQueue <- func() {
errChan <- f()
}
return <-errChan
}
// CallVal queues function f on the main thread and returns a value returned by f.
func CallVal(f func() interface{}) interface{} {
checkRun()
respChan := make(chan interface{})
callQueue <- func() {
respChan <- f()
}
return <-respChan
}

2
vendor/github.com/faiface/pixel/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,2 @@
test
.vscode

23
vendor/github.com/faiface/pixel/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,23 @@
language: go
sudo: false
addons:
apt:
packages:
- xorg-dev
- libx11-dev
- libxrandr-dev
- libxinerama-dev
- libxcursor-dev
- libxi-dev
- libopenal-dev
- libasound2-dev
go:
- 1.8
- 1.7.4
- tip
install:
- go get -t ./...
script:
- go test -i -race ./...
- go test -v -race ./...

14
vendor/github.com/faiface/pixel/CONTRIBUTING.md generated vendored Normal file
View File

@@ -0,0 +1,14 @@
# Contributing to Pixel
:tada: Hi! I'm really glad you're considering contributing to Pixel! :tada:
## Here are a few ways you can contribute
1. **Make a community example** and place it inside the [examples/community](examples/community) folder.
2. **Add tests**. There only few tests in Pixel at the moment. Take a look at them and make some similar.
3. **Add a small feature or an improvement**. Feel like some small feature is missing? Just make a PR. Be ready that I might reject it, though, if I don't find it particularly appealing.
4. **Join the big development** by joining the discussion at the [Gitter](https://gitter.im/pixellib/Lobby), where we can discuss bigger changes and implement them after that.
## How to make a pull request
Go gives you a nice surprise when attempting to make a PR on Github. The thing is, that when user _xyz_ forks Pixel on Github, it ends up in _github.com/xyz/pixel_, which fucks up your import paths. Here's how you deal with that: https://www.reddit.com/r/golang/comments/2jdcw1/how_do_you_deal_with_github_forking/.

21
vendor/github.com/faiface/pixel/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 Michal Štrba
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

160
vendor/github.com/faiface/pixel/README.md generated vendored Normal file
View File

@@ -0,0 +1,160 @@
# Pixel [![Build Status](https://travis-ci.org/faiface/pixel.svg?branch=master)](https://travis-ci.org/faiface/pixel) [![GoDoc](https://godoc.org/github.com/faiface/pixel?status.svg)](https://godoc.org/github.com/faiface/pixel) [![Go Report Card](https://goreportcard.com/badge/github.com/faiface/pixel)](https://goreportcard.com/report/github.com/faiface/pixel) [![Join the chat at https://gitter.im/pixellib/Lobby](https://badges.gitter.im/pixellib/Lobby.svg)](https://gitter.im/pixellib/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
A hand-crafted 2D game library in Go. Take a look into the [features](#features) to see what it can
do.
```
go get github.com/faiface/pixel
```
See [requirements](#requirements) for the list of libraries necessary for compilation.
## Tutorial
The [Wiki of this repo](https://github.com/faiface/pixel/wiki) contains an extensive tutorial
covering several topics of Pixel. Here's the content of the tutorial parts so far:
- [Creating a Window](https://github.com/faiface/pixel/wiki/Creating-a-Window)
- [Drawing a Sprite](https://github.com/faiface/pixel/wiki/Drawing-a-Sprite)
- [Moving, scaling and rotating with Matrix](https://github.com/faiface/pixel/wiki/Moving,-scaling-and-rotating-with-Matrix)
- [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
The [examples](https://github.com/faiface/pixel/tree/master/examples) directory contains a few
examples demonstrating Pixel's functionality.
**To run an example**, navigate to it's directory, then `go run` the `main.go` file. For example:
```
$ cd examples/platformer
$ go run main.go
```
Here are some screenshots from the examples!
| [Lights](examples/lights) | [Platformer](examples/platformer) |
| --- | --- |
| ![Lights](examples/lights/screenshot.png) | ![Platformer](examples/platformer/screenshot.png) |
| [Smoke](examples/smoke) | [Typewriter](examples/typewriter) |
| --- | --- |
| ![Smoke](examples/smoke/screenshot.png) | ![Typewriter](examples/typewriter/screenshot.png) |
| [Raycaster](examples/community/raycaster) | [Starfield](examples/community/starfield) |
| --- | --- |
| ![Raycaster](examples/community/raycaster/screenshot.png) | ![Starfield](examples/community/starfield/screenshot.png) |
## Features
Here's the list of the main features in Pixel. Although Pixel is still under heavy development,
**there should be no major breakage in the API.** This is not a 100% guarantee, though.
- Fast 2D graphics
- Sprites
- Primitive shapes with immediate mode style
[IMDraw](https://github.com/faiface/pixel/wiki/Drawing-shapes-with-IMDraw) (circles, rectangles,
lines, ...)
- Optimized drawing with [Batch](https://github.com/faiface/pixel/wiki/Drawing-efficiently-with-Batch)
- Text drawing with [text](https://godoc.org/github.com/faiface/pixel/text) package
- Audio through a separate [Beep](https://github.com/faiface/beep) library.
- Simple and convenient API
- Drawing a sprite to a window is as simple as `sprite.Draw(window, matrix)`
- Wanna know where the center of a window is? `window.Bounds().Center()`
- [...](https://godoc.org/github.com/faiface/pixel)
- Full documentation and tutorial
- Works on Linux, macOS and Windows
- Window creation and manipulation (resizing, fullscreen, multiple windows, ...)
- Keyboard (key presses, text input) and mouse input without events
- Well integrated with the Go standard library
- Use `"image"` package for loading pictures
- Use `"time"` package for measuring delta time and FPS
- Use `"image/color"` for colors, or use Pixel's own `color.Color` format, which supports easy
multiplication and a few more features
- Pixel uses `float64` throughout the library, compatible with `"math"` package
- Geometry transformations with
[Matrix](https://github.com/faiface/pixel/wiki/Moving,-scaling-and-rotating-with-Matrix)
- Moving, scaling, rotating
- Easy camera implementation
- Off-screen drawing to Canvas or any other target (Batch, IMDraw, ...)
- Fully garbage collected, no `Close` or `Dispose` methods
- Full [Porter-Duff](http://ssp.impulsetrain.com/porterduff.html) composition, which enables
- 2D lighting
- Cutting holes into objects
- Much more...
- Pixel let's you draw stuff and do your job, it doesn't impose any particular style or paradigm
- Platform and backend independent [core](https://godoc.org/github.com/faiface/pixel)
- Core Target/Triangles/Picture pattern makes it easy to create new drawing targets that do
arbitrarily crazy stuff (e.g. graphical effects)
- Small codebase, ~5K lines of code, including the backend [glhf](https://github.com/faiface/glhf)
package
## Missing features
Pixel is in development and still missing few critical features. Here're the most critical ones.
- ~~Audio~~
- ~~Drawing text~~
- Antialiasing (filtering is supported, though)
- ~~Advanced window manipulation (cursor hiding, window icon, ...)~~
- Better support for Hi-DPI displays
- Mobile (and perhaps HTML5?) backend
- More advanced graphical effects (e.g. blur)
- Tests and benchmarks
**Implementing these features will get us to the 1.0 release.** Contribute, so that it's as soon as
possible!
## Requirements
If you're using Windows and having trouble building Pixel, please check [this
guide](https://github.com/faiface/pixel/wiki/Building-Pixel-on-Windows) on the
[wiki](https://github.com/faiface/pixel/wiki).
[PixelGL](https://godoc.org/github.com/faiface/pixel/pixelgl) backend uses OpenGL to render
graphics. Because of that, OpenGL development libraries are needed for compilation. The dependencies
are same as for [GLFW](https://github.com/go-gl/glfw).
The OpenGL version used is **OpenGL 3.3**.
- On macOS, you need Xcode or Command Line Tools for Xcode (`xcode-select --install`) for required
headers and libraries.
- On Ubuntu/Debian-like Linux distributions, you need `libgl1-mesa-dev` and `xorg-dev` packages.
- On CentOS/Fedora-like Linux distributions, you need `libX11-devel libXcursor-devel libXrandr-devel
libXinerama-devel mesa-libGL-devel libXi-devel` packages.
- See [here](http://www.glfw.org/docs/latest/compile.html#compile_deps) for full details.
**The combination of Go 1.8, macOS and latest XCode seems to be problematic** as mentioned in issue
[#7](https://github.com/faiface/pixel/issues/7). This issue is probably not related to Pixel.
**Upgrading to Go 1.8.1 fixes the issue.**
## Contributing
Pixel is in, let's say, mid-stage of development. Many of the important features are here, some are
missing. That's why **contributions are very important and welcome!** All alone, I will be able to
finish the library, but it'll take a lot of time. With your help, it'll take much less. I encourage
everyone to contribute, even with just an idea. Especially welcome are **issues** and **pull
requests**.
**However, I won't accept everything. Pixel is being developed with thought and care.** Each
component was designed and re-designed multiple times. Code and API quality is very important here.
API is focused on simplicity and expressiveness.
When contributing, keep these goals in mind. It doesn't mean that I'll only accept perfect pull
requests. It just means that I might not like your idea. Or that your pull requests could need some
rewriting. That's perfectly fine, don't let it put you off. In the end, we'll just end up with a
better result.
Take a look at [CONTRIBUTING.md](CONTRIBUTING.md) for further information.
For any kind of discussion, feel free to use our
[Gitter](https://gitter.im/pixellib/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
community.
## License
[MIT](LICENSE)

163
vendor/github.com/faiface/pixel/batch.go generated vendored Normal file
View File

@@ -0,0 +1,163 @@
package pixel
import (
"fmt"
"image/color"
)
// Batch is a Target that allows for efficient drawing of many objects with the same Picture.
//
// To put an object into a Batch, just draw it onto it:
// object.Draw(batch)
type Batch struct {
cont Drawer
mat Matrix
col RGBA
}
var _ BasicTarget = (*Batch)(nil)
// NewBatch creates an empty Batch with the specified Picture and container.
//
// The container is where objects get accumulated. Batch will support precisely those Triangles
// properties, that the supplied container supports. If you retain access to the container and
// change it, call Dirty to notify Batch about the change.
//
// Note, that if the container does not support TrianglesColor, color masking will not work.
func NewBatch(container Triangles, pic Picture) *Batch {
b := &Batch{cont: Drawer{Triangles: container, Picture: pic}}
b.SetMatrix(IM)
b.SetColorMask(Alpha(1))
return b
}
// Dirty notifies Batch about an external modification of it's container. If you retain access to
// the Batch's container and change it, call Dirty to notify Batch about the change.
//
// container := &pixel.TrianglesData{}
// batch := pixel.NewBatch(container, nil)
// container.SetLen(10) // container changed from outside of Batch
// batch.Dirty() // notify Batch about the change
func (b *Batch) Dirty() {
b.cont.Dirty()
}
// Clear removes all objects from the Batch.
func (b *Batch) Clear() {
b.cont.Triangles.SetLen(0)
b.cont.Dirty()
}
// Draw draws all objects that are currently in the Batch onto another Target.
func (b *Batch) Draw(t Target) {
b.cont.Draw(t)
}
// SetMatrix sets a Matrix that every point will be projected by.
func (b *Batch) SetMatrix(m Matrix) {
b.mat = m
}
// SetColorMask sets a mask color used in the following draws onto the Batch.
func (b *Batch) SetColorMask(c color.Color) {
if c == nil {
b.col = Alpha(1)
return
}
b.col = ToRGBA(c)
}
// MakeTriangles returns a specialized copy of the provided Triangles that draws onto this Batch.
func (b *Batch) MakeTriangles(t Triangles) TargetTriangles {
bt := &batchTriangles{
tri: t.Copy(),
tmp: MakeTrianglesData(t.Len()),
dst: b,
}
return bt
}
// MakePicture returns a specialized copy of the provided Picture that draws onto this Batch.
func (b *Batch) MakePicture(p Picture) TargetPicture {
if p != b.cont.Picture {
panic(fmt.Errorf("(%T).MakePicture: Picture is not the Batch's Picture", b))
}
bp := &batchPicture{
pic: p,
dst: b,
}
return bp
}
type batchTriangles struct {
tri Triangles
tmp *TrianglesData
dst *Batch
}
func (bt *batchTriangles) Len() int {
return bt.tri.Len()
}
func (bt *batchTriangles) SetLen(len int) {
bt.tri.SetLen(len)
bt.tmp.SetLen(len)
}
func (bt *batchTriangles) Slice(i, j int) Triangles {
return &batchTriangles{
tri: bt.tri.Slice(i, j),
tmp: bt.tmp.Slice(i, j).(*TrianglesData),
dst: bt.dst,
}
}
func (bt *batchTriangles) Update(t Triangles) {
bt.tri.Update(t)
}
func (bt *batchTriangles) Copy() Triangles {
return &batchTriangles{
tri: bt.tri.Copy(),
tmp: bt.tmp.Copy().(*TrianglesData),
dst: bt.dst,
}
}
func (bt *batchTriangles) draw(bp *batchPicture) {
bt.tmp.Update(bt.tri)
for i := range *bt.tmp {
(*bt.tmp)[i].Position = bt.dst.mat.Project((*bt.tmp)[i].Position)
(*bt.tmp)[i].Color = bt.dst.col.Mul((*bt.tmp)[i].Color)
}
cont := bt.dst.cont.Triangles
cont.SetLen(cont.Len() + bt.tri.Len())
added := cont.Slice(cont.Len()-bt.tri.Len(), cont.Len())
added.Update(bt.tri)
added.Update(bt.tmp)
bt.dst.cont.Dirty()
}
func (bt *batchTriangles) Draw() {
bt.draw(nil)
}
type batchPicture struct {
pic Picture
dst *Batch
}
func (bp *batchPicture) Bounds() Rect {
return bp.pic.Bounds()
}
func (bp *batchPicture) Draw(t TargetTriangles) {
bt := t.(*batchTriangles)
if bp.dst != bt.dst {
panic(fmt.Errorf("(%T).Draw: TargetTriangles generated by different Batch", bp))
}
bt.draw(bp)
}

97
vendor/github.com/faiface/pixel/color.go generated vendored Normal file
View File

@@ -0,0 +1,97 @@
package pixel
import "image/color"
// RGBA represents an alpha-premultiplied RGBA color with components within range [0, 1].
//
// The difference between color.RGBA is that the value range is [0, 1] and the values are floats.
type RGBA struct {
R, G, B, A float64
}
// RGB returns a fully opaque RGBA color with the given RGB values.
//
// A common way to construct a transparent color is to create one with RGB constructor, then
// multiply it by a color obtained from the Alpha constructor.
func RGB(r, g, b float64) RGBA {
return RGBA{r, g, b, 1}
}
// Alpha returns a white RGBA color with the given alpha component.
func Alpha(a float64) RGBA {
return RGBA{a, a, a, a}
}
// Add adds color d to color c component-wise and returns the result (the components are not
// clamped).
func (c RGBA) Add(d RGBA) RGBA {
return RGBA{
R: c.R + d.R,
G: c.G + d.G,
B: c.B + d.B,
A: c.A + d.A,
}
}
// Sub subtracts color d from color c component-wise and returns the result (the components
// are not clamped).
func (c RGBA) Sub(d RGBA) RGBA {
return RGBA{
R: c.R - d.R,
G: c.G - d.G,
B: c.B - d.B,
A: c.A - d.A,
}
}
// Mul multiplies color c by color d component-wise (the components are not clamped).
func (c RGBA) Mul(d RGBA) RGBA {
return RGBA{
R: c.R * d.R,
G: c.G * d.G,
B: c.B * d.B,
A: c.A * d.A,
}
}
// Scaled multiplies each component of color c by scale and returns the result (the components
// are not clamped).
func (c RGBA) Scaled(scale float64) RGBA {
return RGBA{
R: c.R * scale,
G: c.G * scale,
B: c.B * scale,
A: c.A * scale,
}
}
// RGBA returns alpha-premultiplied red, green, blue and alpha components of the RGBA color.
func (c RGBA) RGBA() (r, g, b, a uint32) {
r = uint32(0xffff * c.R)
g = uint32(0xffff * c.G)
b = uint32(0xffff * c.B)
a = uint32(0xffff * c.A)
return
}
// ToRGBA converts a color to RGBA format. Using this function is preferred to using RGBAModel, for
// performance (using RGBAModel introduces additional unnecessary allocations).
func ToRGBA(c color.Color) RGBA {
if c, ok := c.(RGBA); ok {
return c
}
r, g, b, a := c.RGBA()
return RGBA{
float64(r) / 0xffff,
float64(g) / 0xffff,
float64(b) / 0xffff,
float64(a) / 0xffff,
}
}
// RGBAModel converts colors to RGBA format.
var RGBAModel = color.ModelFunc(rgbaModel)
func rgbaModel(c color.Color) color.Color {
return ToRGBA(c)
}

65
vendor/github.com/faiface/pixel/compose.go generated vendored Normal file
View File

@@ -0,0 +1,65 @@
package pixel
import "errors"
// ComposeTarget is a BasicTarget capable of Porter-Duff composition.
type ComposeTarget interface {
BasicTarget
// SetComposeMethod sets a Porter-Duff composition method to be used.
SetComposeMethod(ComposeMethod)
}
// ComposeMethod is a Porter-Duff composition method.
type ComposeMethod int
// Here's the list of all available Porter-Duff composition methods. Use ComposeOver for the basic
// alpha blending.
const (
ComposeOver ComposeMethod = iota
ComposeIn
ComposeOut
ComposeAtop
ComposeRover
ComposeRin
ComposeRout
ComposeRatop
ComposeXor
ComposePlus
ComposeCopy
)
// Compose composes two colors together according to the ComposeMethod. A is the foreground, B is
// the background.
func (cm ComposeMethod) Compose(a, b RGBA) RGBA {
var fa, fb float64
switch cm {
case ComposeOver:
fa, fb = 1, 1-a.A
case ComposeIn:
fa, fb = b.A, 0
case ComposeOut:
fa, fb = 1-b.A, 0
case ComposeAtop:
fa, fb = b.A, 1-a.A
case ComposeRover:
fa, fb = 1-b.A, 1
case ComposeRin:
fa, fb = 0, a.A
case ComposeRout:
fa, fb = 0, 1-a.A
case ComposeRatop:
fa, fb = 1-b.A, a.A
case ComposeXor:
fa, fb = 1-b.A, 1-a.A
case ComposePlus:
fa, fb = 1, 1
case ComposeCopy:
fa, fb = 1, 0
default:
panic(errors.New("Compose: invalid ComposeMethod"))
}
return a.Mul(Alpha(fa)).Add(b.Mul(Alpha(fb)))
}

276
vendor/github.com/faiface/pixel/data.go generated vendored Normal file
View File

@@ -0,0 +1,276 @@
package pixel
import (
"fmt"
"image"
"image/color"
"image/draw"
"math"
)
// TrianglesData specifies a list of Triangles vertices with three common properties:
// TrianglesPosition, TrianglesColor and TrianglesPicture.
type TrianglesData []struct {
Position Vec
Color RGBA
Picture Vec
Intensity float64
}
// 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 the 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, (0, 0), 0).
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 RGBA
Picture Vec
Intensity float64
}{Color: RGBA{1, 1, 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, (*td)[i].Intensity = 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) RGBA {
return (*td)[i].Color
}
// Picture returns the picture property of i-th vertex.
func (td *TrianglesData) Picture(i int) (pic Vec, intensity float64) {
return (*td)[i].Picture, (*td)[i].Intensity
}
// PictureData specifies an in-memory rectangular area of 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.
//
// The format of the pixels is color.RGBA and not pixel.RGBA for a very serious reason:
// pixel.RGBA takes up 8x more memory than color.RGBA.
type PictureData struct {
Pix []color.RGBA
Stride int
Rect Rect
}
// MakePictureData creates a zero-initialized PictureData covering the given rectangle.
func MakePictureData(rect Rect) *PictureData {
w := int(math.Ceil(rect.Max.X)) - int(math.Floor(rect.Min.X))
h := int(math.Ceil(rect.Max.Y)) - int(math.Floor(rect.Min.Y))
pd := &PictureData{
Stride: w,
Rect: rect,
}
pd.Pix = make([]color.RGBA, w*h)
return pd
}
func verticalFlip(rgba *image.RGBA) {
bounds := rgba.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 := rgba.Pix[i*rgba.Stride : i*rgba.Stride+width*4]
jRow := rgba.Pix[j*rgba.Stride : j*rgba.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 {
var rgba *image.RGBA
if rgbaImg, ok := img.(*image.RGBA); ok {
rgba = rgbaImg
} else {
rgba = image.NewRGBA(img.Bounds())
draw.Draw(rgba, rgba.Bounds(), img, img.Bounds().Min, draw.Src)
}
verticalFlip(rgba)
pd := MakePictureData(R(
float64(rgba.Bounds().Min.X),
float64(rgba.Bounds().Min.Y),
float64(rgba.Bounds().Max.X),
float64(rgba.Bounds().Max.Y),
))
for i := range pd.Pix {
pd.Pix[i].R = rgba.Pix[i*4+0]
pd.Pix[i].G = rgba.Pix[i*4+1]
pd.Pix[i].B = rgba.Pix[i*4+2]
pd.Pix[i].A = rgba.Pix[i*4+3]
}
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 := math.Floor(bounds.Min.Y); y < bounds.Max.Y; y++ {
for x := math.Floor(bounds.Min.X); x < bounds.Max.X; x++ {
// this together with the Floor is a trick to get all of the pixels
at := V(
math.Max(x, bounds.Min.X),
math.Max(y, bounds.Min.Y),
)
col := pic.Color(at)
pd.Pix[pd.Index(at)] = color.RGBA{
R: uint8(col.R * 255),
G: uint8(col.G * 255),
B: uint8(col.B * 255),
A: uint8(col.A * 255),
}
}
}
}
return pd
}
// Image converts PictureData into an image.RGBA.
//
// The resulting image.RGBA's Bounds will be equivalent of the PictureData's Bounds.
func (pd *PictureData) Image() *image.RGBA {
bounds := image.Rect(
int(math.Floor(pd.Rect.Min.X)),
int(math.Floor(pd.Rect.Min.Y)),
int(math.Ceil(pd.Rect.Max.X)),
int(math.Ceil(pd.Rect.Max.Y)),
)
rgba := image.NewRGBA(bounds)
i := 0
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
for x := bounds.Min.X; x < bounds.Max.X; x++ {
off := pd.Index(V(float64(x), float64(y)))
rgba.Pix[i*4+0] = pd.Pix[off].R
rgba.Pix[i*4+1] = pd.Pix[off].G
rgba.Pix[i*4+2] = pd.Pix[off].B
rgba.Pix[i*4+3] = pd.Pix[off].A
i++
}
}
verticalFlip(rgba)
return rgba
}
// Index returns the index of the pixel at the specified position inside the Pix slice.
func (pd *PictureData) Index(at Vec) int {
at = at.Sub(pd.Rect.Min.Map(math.Floor))
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
}
// Color returns the color located at the given position.
func (pd *PictureData) Color(at Vec) RGBA {
if !pd.Rect.Contains(at) {
return RGBA{0, 0, 0, 0}
}
return ToRGBA(pd.Pix[pd.Index(at)])
}

7
vendor/github.com/faiface/pixel/doc.go generated vendored Normal file
View File

@@ -0,0 +1,7 @@
// Package pixel implements platform and backend agnostic core of the Pixel game development
// library.
//
// It specifies the core Target, Triangles, Picture pattern and implements standard elements, such
// as Sprite, Batch, Vec, Matrix and RGBA in addition to the basic Triangles and Picture
// implementations: TrianglesData and PictureData.
package pixel

96
vendor/github.com/faiface/pixel/drawer.go generated vendored Normal file
View File

@@ -0,0 +1,96 @@
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 the Picture.
//
// Note, that Drawer caches the results of MakePicture from Targets it's drawn to for each Picture
// it's set to. What it means is that using a Drawer with an unbounded number of Pictures leads to a
// memory leak, since Drawer caches them and never forgets. In such a situation, create a new Drawer
// for each Picture.
type Drawer struct {
Triangles Triangles
Picture Picture
targets map[Target]*drawerTarget
inited bool
}
type drawerTarget struct {
tris TargetTriangles
pics map[Picture]TargetPicture
clean bool
}
func (d *Drawer) lazyInit() {
if !d.inited {
d.targets = make(map[Target]*drawerTarget)
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.targets {
t.clean = 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
}
dt := d.targets[t]
if dt == nil {
dt = &drawerTarget{
pics: make(map[Picture]TargetPicture),
}
d.targets[t] = dt
}
if dt.tris == nil {
dt.tris = t.MakeTriangles(d.Triangles)
dt.clean = true
}
if !dt.clean {
dt.tris.SetLen(d.Triangles.Len())
dt.tris.Update(d.Triangles)
dt.clean = true
}
if d.Picture == nil {
dt.tris.Draw()
return
}
pic := dt.pics[d.Picture]
if pic == nil {
pic = t.MakePicture(d.Picture)
dt.pics[d.Picture] = pic
}
pic.Draw(dt.tris)
}

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 stephen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

405
vendor/github.com/faiface/pixel/geometry.go generated vendored Normal file
View File

@@ -0,0 +1,405 @@
package pixel
import (
"fmt"
"math"
)
// Clamp returns x clamped to the interval [min, max].
//
// If x is less than min, min is returned. If x is more than max, max is returned. Otherwise, x is
// returned.
func Clamp(x, min, max float64) float64 {
if x < min {
return min
}
if x > max {
return max
}
return x
}
// Vec is a 2D vector type with X and Y coordinates.
//
// Create vectors with the V constructor:
//
// u := pixel.V(1, 2)
// v := pixel.V(8, -3)
//
// Use various methods to manipulate them:
//
// w := u.Add(v)
// fmt.Println(w) // Vec(9, -1)
// fmt.Println(u.Sub(v)) // Vec(-7, 5)
// u = pixel.V(2, 3)
// v = pixel.V(8, 1)
// if u.X < 0 {
// fmt.Println("this won't happen")
// }
// x := u.Unit().Dot(v.Unit())
type Vec struct {
X, Y float64
}
// ZV is a zero vector.
var ZV = Vec{0, 0}
// V returns a new 2D vector with the given coordinates.
func V(x, y float64) Vec {
return Vec{x, y}
}
// Unit returns a vector of length 1 facing the given angle.
func Unit(angle float64) Vec {
return Vec{1, 0}.Rotated(angle)
}
// String returns the string representation of the vector u.
//
// u := pixel.V(4.5, -1.3)
// u.String() // returns "Vec(4.5, -1.3)"
// fmt.Println(u) // Vec(4.5, -1.3)
func (u Vec) String() string {
return fmt.Sprintf("Vec(%v, %v)", u.X, u.Y)
}
// XY returns the components of the vector in two return values.
func (u Vec) XY() (x, y float64) {
return u.X, u.Y
}
// Add returns the sum of vectors u and v.
func (u Vec) Add(v Vec) Vec {
return Vec{
u.X + v.X,
u.Y + v.Y,
}
}
// Sub returns the difference betweeen vectors u and v.
func (u Vec) Sub(v Vec) Vec {
return Vec{
u.X - v.X,
u.Y - v.Y,
}
}
// To returns the vector from u to v. Equivalent to v.Sub(u).
func (u Vec) To(v Vec) Vec {
return Vec{
v.X - u.X,
v.Y - u.Y,
}
}
// Scaled returns the vector u multiplied by c.
func (u Vec) Scaled(c float64) Vec {
return Vec{u.X * c, u.Y * c}
}
// ScaledXY returns the vector u multiplied by the vector v component-wise.
func (u Vec) ScaledXY(v Vec) Vec {
return Vec{u.X * v.X, u.Y * v.Y}
}
// Len returns the length of the vector u.
func (u Vec) Len() float64 {
return math.Hypot(u.X, u.Y)
}
// Angle returns the angle between the vector u and the x-axis. The result is in range [-Pi, Pi].
func (u Vec) Angle() float64 {
return math.Atan2(u.Y, u.X)
}
// Unit returns a vector of length 1 facing the direction of u (has the same angle).
func (u Vec) Unit() Vec {
if u.X == 0 && u.Y == 0 {
return Vec{1, 0}
}
return u.Scaled(1 / u.Len())
}
// Rotated returns the vector u rotated by the given angle in radians.
func (u Vec) Rotated(angle float64) Vec {
sin, cos := math.Sincos(angle)
return Vec{
u.X*cos - u.Y*sin,
u.X*sin + u.Y*cos,
}
}
// Normal returns a vector normal to u. Equivalent to u.Rotated(math.Pi / 2), but faster.
func (u Vec) Normal() Vec {
return Vec{-u.Y, u.X}
}
// Dot returns the dot product of vectors u and v.
func (u Vec) Dot(v Vec) float64 {
return u.X*v.X + u.Y*v.Y
}
// Cross return the cross product of vectors u and v.
func (u Vec) Cross(v Vec) float64 {
return u.X*v.Y - v.X*u.Y
}
// Project returns a projection (or component) of vector u in the direction of vector v.
//
// Behaviour is undefined if v is a zero vector.
func (u Vec) Project(v Vec) Vec {
len := u.Dot(v) / v.Len()
return v.Unit().Scaled(len)
}
// Map applies the function f to both x and y components of the vector u and returns the modified
// vector.
//
// u := pixel.V(10.5, -1.5)
// v := u.Map(math.Floor) // v is Vec(10, -2), both components of u floored
func (u Vec) Map(f func(float64) float64) Vec {
return Vec{
f(u.X),
f(u.Y),
}
}
// Lerp returns a linear interpolation between vectors a and b.
//
// This function basically returns a point along the line between a and b and t chooses which one.
// If t is 0, then a will be returned, if t is 1, b will be returned. Anything between 0 and 1 will
// return the appropriate point between a and b and so on.
func Lerp(a, b Vec, t float64) Vec {
return a.Scaled(1 - t).Add(b.Scaled(t))
}
// Rect is a 2D rectangle aligned with the axes of the coordinate system. It is defined by two
// points, Min and Max.
//
// The invariant should hold, that Max's components are greater or equal than Min's components
// respectively.
type Rect struct {
Min, Max Vec
}
// R returns a new Rect with given the Min and Max coordinates.
//
// Note that the returned rectangle is not automatically normalized.
func R(minX, minY, maxX, maxY float64) Rect {
return Rect{
Min: Vec{minX, minY},
Max: Vec{maxX, maxY},
}
}
// String returns the string representation of the Rect.
//
// r := pixel.R(100, 50, 200, 300)
// r.String() // returns "Rect(100, 50, 200, 300)"
// fmt.Println(r) // Rect(100, 50, 200, 300)
func (r Rect) String() string {
return fmt.Sprintf("Rect(%v, %v, %v, %v)", r.Min.X, r.Min.Y, r.Max.X, r.Max.Y)
}
// Norm returns the Rect in normal form, such that Max is component-wise greater or equal than Min.
func (r Rect) Norm() Rect {
return Rect{
Min: Vec{
math.Min(r.Min.X, r.Max.X),
math.Min(r.Min.Y, r.Max.Y),
},
Max: Vec{
math.Max(r.Min.X, r.Max.X),
math.Max(r.Min.Y, r.Max.Y),
},
}
}
// W returns the width of the Rect.
func (r Rect) W() float64 {
return r.Max.X - r.Min.X
}
// H returns the height of the Rect.
func (r Rect) H() float64 {
return r.Max.Y - r.Min.Y
}
// Size returns the vector of width and height of the Rect.
func (r Rect) Size() Vec {
return V(r.W(), r.H())
}
// Area returns the area of r. If r is not normalized, area may be negative.
func (r Rect) Area() float64 {
return r.W() * r.H()
}
// Center returns the position of the center of the Rect.
func (r Rect) Center() Vec {
return Lerp(r.Min, r.Max, 0.5)
}
// Moved returns the Rect moved (both Min and Max) by the given vector delta.
func (r Rect) Moved(delta Vec) Rect {
return Rect{
Min: r.Min.Add(delta),
Max: r.Max.Add(delta),
}
}
// Resized returns the Rect resized to the given size while keeping the position of the given
// anchor.
//
// r.Resized(r.Min, size) // resizes while keeping the position of the lower-left corner
// r.Resized(r.Max, size) // same with the top-right corner
// r.Resized(r.Center(), size) // resizes around the center
//
// This function does not make sense for resizing a rectangle of zero area and will panic. Use
// ResizedMin in the case of zero area.
func (r Rect) Resized(anchor, size Vec) Rect {
if r.W()*r.H() == 0 {
panic(fmt.Errorf("(%T).Resize: zero area", r))
}
fraction := Vec{size.X / r.W(), size.Y / r.H()}
return Rect{
Min: anchor.Add(r.Min.Sub(anchor).ScaledXY(fraction)),
Max: anchor.Add(r.Max.Sub(anchor).ScaledXY(fraction)),
}
}
// ResizedMin returns the Rect resized to the given size while keeping the position of the Rect's
// Min.
//
// Sizes of zero area are safe here.
func (r Rect) ResizedMin(size Vec) Rect {
return Rect{
Min: r.Min,
Max: r.Min.Add(size),
}
}
// Contains checks whether a vector u is contained within this Rect (including it's borders).
func (r Rect) Contains(u Vec) bool {
return r.Min.X <= u.X && u.X <= r.Max.X && r.Min.Y <= u.Y && u.Y <= r.Max.Y
}
// Union returns the minimal Rect which covers both r and s. Rects r and s must be normalized.
func (r Rect) Union(s Rect) Rect {
return R(
math.Min(r.Min.X, s.Min.X),
math.Min(r.Min.Y, s.Min.Y),
math.Max(r.Max.X, s.Max.X),
math.Max(r.Max.Y, s.Max.Y),
)
}
// Intersect returns the maximal Rect which is covered by both r and s. Rects r and s must be normalized.
//
// If r and s don't overlap, this function returns R(0, 0, 0, 0).
func (r Rect) Intersect(s Rect) Rect {
t := R(
math.Max(r.Min.X, s.Min.X),
math.Max(r.Min.Y, s.Min.Y),
math.Min(r.Max.X, s.Max.X),
math.Min(r.Max.Y, s.Max.Y),
)
if t.Min.X >= t.Max.X || t.Min.Y >= t.Max.Y {
return Rect{}
}
return t
}
// 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
// example:
//
// pixel.IM.Moved(pixel.V(100, 200)).Rotated(pixel.ZV, math.Pi/2)
//
// 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.
//
// 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{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)
func (m Matrix) String() string {
return fmt.Sprintf(
"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 {
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 {
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.
func (m Matrix) Scaled(around Vec, scale float64) Matrix {
return m.ScaledXY(around, V(scale, scale))
}
// Rotated rotates everything around a given point by the given angle in radians.
func (m Matrix) Rotated(around Vec, angle float64) Matrix {
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 {
return Matrix{
next[0]*m[0] + next[2]*m[1],
next[1]*m[0] + next[3]*m[1],
next[0]*m[2] + next[2]*m[3],
next[1]*m[2] + next[3]*m[3],
next[0]*m[4] + next[2]*m[5] + next[4],
next[1]*m[4] + next[3]*m[5] + next[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 {
return Vec{m[0]*u.X + m[2]*u.Y + m[4], 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 {
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]}
}

627
vendor/github.com/faiface/pixel/imdraw/imdraw.go generated vendored Normal file
View File

@@ -0,0 +1,627 @@
// Package imdraw implements a basic primitive geometry shape and pictured polygon drawing for Pixel
// with a nice immediate-mode-like API.
package imdraw
import (
"image/color"
"math"
"github.com/faiface/pixel"
)
// IMDraw is an immediate-mode-like shape drawer and BasicTarget. IMDraw supports TrianglesPosition,
// TrianglesColor, TrianglesPicture and PictureColor.
//
// IMDraw, other than a regular BasicTarget, is used to draw shapes. To draw shapes, you first need
// to Push some points to IMDraw:
//
// imd := pixel.NewIMDraw(pic) // use nil pic if you only want to draw primitive shapes
// imd.Push(pixel.V(100, 100))
// imd.Push(pixel.V(500, 100))
//
// Once you have Pushed some points, you can use them to draw a shape, such as a line:
//
// imd.Line(20) // draws a 20 units thick line
//
// Set exported fields to change properties of Pushed points:
//
// imd.Color = pixel.RGB(1, 0, 0)
// imd.Push(pixel.V(200, 200))
// imd.Circle(400, 0)
//
// Here is the list of all available point properties (need to be set before Pushing a point):
// - Color - applies to all
// - Picture - coordinates, only applies to filled polygons
// - Intensity - picture intensity, only applies to filled polygons
// - Precision - curve drawing precision, only applies to circles and ellipses
// - EndShape - shape of the end of a line, only applies to lines and outlines
//
// And here's the list of all shapes that can be drawn (all, except for line, can be filled or
// outlined):
// - Line
// - Polygon
// - Circle
// - Circle arc
// - Ellipse
// - Ellipse arc
type IMDraw struct {
Color color.Color
Picture pixel.Vec
Intensity float64
Precision int
EndShape EndShape
points []point
pool [][]point
matrix pixel.Matrix
mask pixel.RGBA
tri *pixel.TrianglesData
batch *pixel.Batch
}
var _ pixel.BasicTarget = (*IMDraw)(nil)
type point struct {
pos pixel.Vec
col pixel.RGBA
pic pixel.Vec
in float64
precision int
endshape EndShape
}
// EndShape specifies the shape of an end of a line or a curve.
type EndShape int
const (
// NoEndShape leaves a line point with no special end shape.
NoEndShape EndShape = iota
// SharpEndShape is a sharp triangular end shape.
SharpEndShape
// RoundEndShape is a circular end shape.
RoundEndShape
)
// New creates a new empty IMDraw. An optional Picture can be used to draw with a Picture.
//
// If you just want to draw primitive shapes, pass nil as the Picture.
func New(pic pixel.Picture) *IMDraw {
tri := &pixel.TrianglesData{}
im := &IMDraw{
tri: tri,
batch: pixel.NewBatch(tri, pic),
}
im.SetMatrix(pixel.IM)
im.SetColorMask(pixel.Alpha(1))
im.Reset()
return im
}
// Clear removes all drawn shapes from the IM. This does not remove Pushed points.
func (imd *IMDraw) Clear() {
imd.tri.SetLen(0)
imd.batch.Dirty()
}
// Reset restores all point properties to defaults and removes all Pushed points.
//
// This does not affect matrix and color mask set by SetMatrix and SetColorMask.
func (imd *IMDraw) Reset() {
imd.points = imd.points[:0]
imd.Color = pixel.Alpha(1)
imd.Picture = pixel.ZV
imd.Intensity = 0
imd.Precision = 64
imd.EndShape = NoEndShape
}
// Draw draws all currently drawn shapes inside the IM onto another Target.
//
// Note, that IMDraw's matrix and color mask have no effect here.
func (imd *IMDraw) Draw(t pixel.Target) {
imd.batch.Draw(t)
}
// Push adds some points to the IM queue. All Pushed points will have the same properties except for
// the position.
func (imd *IMDraw) Push(pts ...pixel.Vec) {
if _, ok := imd.Color.(pixel.RGBA); !ok {
imd.Color = pixel.ToRGBA(imd.Color)
}
opts := point{
col: imd.Color.(pixel.RGBA),
pic: imd.Picture,
in: imd.Intensity,
precision: imd.Precision,
endshape: imd.EndShape,
}
for _, pt := range pts {
imd.pushPt(pt, opts)
}
}
func (imd *IMDraw) pushPt(pos pixel.Vec, pt point) {
pt.pos = pos
imd.points = append(imd.points, pt)
}
// SetMatrix sets a Matrix that all further points will be transformed by.
func (imd *IMDraw) SetMatrix(m pixel.Matrix) {
imd.matrix = m
imd.batch.SetMatrix(imd.matrix)
}
// SetColorMask sets a color that all further point's color will be multiplied by.
func (imd *IMDraw) SetColorMask(color color.Color) {
imd.mask = pixel.ToRGBA(color)
imd.batch.SetColorMask(imd.mask)
}
// MakeTriangles returns a specialized copy of the provided Triangles that draws onto this IMDraw.
func (imd *IMDraw) MakeTriangles(t pixel.Triangles) pixel.TargetTriangles {
return imd.batch.MakeTriangles(t)
}
// MakePicture returns a specialized copy of the provided Picture that draws onto this IMDraw.
func (imd *IMDraw) MakePicture(p pixel.Picture) pixel.TargetPicture {
return imd.batch.MakePicture(p)
}
// Line draws a polyline of the specified thickness between the Pushed points.
func (imd *IMDraw) Line(thickness float64) {
imd.polyline(thickness, false)
}
// Rectangle draws a rectangle between each two subsequent Pushed points. Drawing a rectangle
// between two points means drawing a rectangle with sides parallel to the axes of the coordinate
// system, where the two points specify it's two opposite corners.
//
// If the thickness is 0, rectangles will be filled, otherwise will be outlined with the given
// thickness.
func (imd *IMDraw) Rectangle(thickness float64) {
if thickness == 0 {
imd.fillRectangle()
} else {
imd.outlineRectangle(thickness)
}
}
// Polygon draws a polygon from the Pushed points. If the thickness is 0, the convex polygon will be
// filled. Otherwise, an outline of the specified thickness will be drawn. The outline does not have
// to be convex.
//
// Note, that the filled polygon does not have to be strictly convex. The way it's drawn is that a
// triangle is drawn between each two adjacent points and the first Pushed point. You can use this
// property to draw certain kinds of concave polygons.
func (imd *IMDraw) Polygon(thickness float64) {
if thickness == 0 {
imd.fillPolygon()
} else {
imd.polyline(thickness, true)
}
}
// Circle draws a circle of the specified radius around each Pushed point. If the thickness is 0,
// the circle will be filled, otherwise a circle outline of the specified thickness will be drawn.
func (imd *IMDraw) Circle(radius, thickness float64) {
if thickness == 0 {
imd.fillEllipseArc(pixel.V(radius, radius), 0, 2*math.Pi)
} else {
imd.outlineEllipseArc(pixel.V(radius, radius), 0, 2*math.Pi, thickness, false)
}
}
// CircleArc draws a circle arc of the specified radius around each Pushed point. If the thickness
// is 0, the arc will be filled, otherwise will be outlined. The arc starts at the low angle and
// continues to the high angle. If low<high, the arc will be drawn counterclockwise. Otherwise it
// will be clockwise. The angles are not normalized by any means.
//
// imd.CircleArc(40, 0, 8*math.Pi, 0)
//
// This line will fill the whole circle 4 times.
func (imd *IMDraw) CircleArc(radius, low, high, thickness float64) {
if thickness == 0 {
imd.fillEllipseArc(pixel.V(radius, radius), low, high)
} else {
imd.outlineEllipseArc(pixel.V(radius, radius), low, high, thickness, true)
}
}
// Ellipse draws an ellipse of the specified radius in each axis around each Pushed points. If the
// thickness is 0, the ellipse will be filled, otherwise an ellipse outline of the specified
// thickness will be drawn.
func (imd *IMDraw) Ellipse(radius pixel.Vec, thickness float64) {
if thickness == 0 {
imd.fillEllipseArc(radius, 0, 2*math.Pi)
} else {
imd.outlineEllipseArc(radius, 0, 2*math.Pi, thickness, false)
}
}
// EllipseArc draws an ellipse arc of the specified radius in each axis around each Pushed point. If
// the thickness is 0, the arc will be filled, otherwise will be outlined. The arc starts at the low
// angle and continues to the high angle. If low<high, the arc will be drawn counterclockwise.
// Otherwise it will be clockwise. The angles are not normalized by any means.
//
// imd.EllipseArc(pixel.V(100, 50), 0, 8*math.Pi, 0)
//
// This line will fill the whole ellipse 4 times.
func (imd *IMDraw) EllipseArc(radius pixel.Vec, low, high, thickness float64) {
if thickness == 0 {
imd.fillEllipseArc(radius, low, high)
} else {
imd.outlineEllipseArc(radius, low, high, thickness, true)
}
}
func (imd *IMDraw) getAndClearPoints() []point {
points := imd.points
// 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][:0]
imd.pool = imd.pool[: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)
(*imd.tri)[off+i].Color = imd.mask.Mul((*imd.tri)[off+i].Color)
}
}
func (imd *IMDraw) fillRectangle() {
points := imd.getAndClearPoints()
if len(points) < 2 {
imd.restorePoints(points)
return
}
off := imd.tri.Len()
imd.tri.SetLen(imd.tri.Len() + 6*(len(points)-1))
for i, j := 0, off; i+1 < len(points); i, j = i+1, j+6 {
a, b := points[i], points[i+1]
c := point{
pos: pixel.V(a.pos.X, b.pos.Y),
col: a.col.Add(b.col).Mul(pixel.Alpha(0.5)),
pic: pixel.V(a.pic.X, b.pic.Y),
in: (a.in + b.in) / 2,
}
d := point{
pos: pixel.V(b.pos.X, a.pos.Y),
col: a.col.Add(b.col).Mul(pixel.Alpha(0.5)),
pic: pixel.V(b.pic.X, a.pic.Y),
in: (a.in + b.in) / 2,
}
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
(*imd.tri)[j+k].Intensity = p.in
}
}
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
}
for i := 0; i+1 < len(points); i++ {
a, b := points[i], points[i+1]
mid := a
mid.col = a.col.Add(b.col).Mul(pixel.Alpha(0.5))
mid.in = (a.in + b.in) / 2
imd.pushPt(a.pos, a)
imd.pushPt(pixel.V(a.pos.X, b.pos.Y), mid)
imd.pushPt(b.pos, b)
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
}
off := imd.tri.Len()
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} {
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
}
}
imd.applyMatrixAndMask(off)
imd.batch.Dirty()
imd.restorePoints(points)
}
func (imd *IMDraw) fillEllipseArc(radius pixel.Vec, low, high float64) {
points := imd.getAndClearPoints()
for _, pt := range points {
num := math.Ceil(math.Abs(high-low) / (2 * math.Pi) * float64(pt.precision))
delta := (high - low) / num
off := imd.tri.Len()
imd.tri.SetLen(imd.tri.Len() + 3*int(num))
for i := range (*imd.tri)[off:] {
(*imd.tri)[off+i].Color = pt.col
(*imd.tri)[off+i].Picture = pixel.ZV
(*imd.tri)[off+i].Intensity = 0
}
for i, j := 0.0, off; i < num; i, j = i+1, j+3 {
angle := low + i*delta
sin, cos := math.Sincos(angle)
a := pt.pos.Add(pixel.V(
radius.X*cos,
radius.Y*sin,
))
angle = low + (i+1)*delta
sin, cos = math.Sincos(angle)
b := pt.pos.Add(pixel.V(
radius.X*cos,
radius.Y*sin,
))
(*imd.tri)[j+0].Position = pt.pos
(*imd.tri)[j+1].Position = a
(*imd.tri)[j+2].Position = b
}
imd.applyMatrixAndMask(off)
imd.batch.Dirty()
}
imd.restorePoints(points)
}
func (imd *IMDraw) outlineEllipseArc(radius pixel.Vec, low, high, thickness float64, doEndShape bool) {
points := imd.getAndClearPoints()
for _, pt := range points {
num := math.Ceil(math.Abs(high-low) / (2 * math.Pi) * float64(pt.precision))
delta := (high - low) / num
off := imd.tri.Len()
imd.tri.SetLen(imd.tri.Len() + 6*int(num))
for i := range (*imd.tri)[off:] {
(*imd.tri)[off+i].Color = pt.col
(*imd.tri)[off+i].Picture = pixel.ZV
(*imd.tri)[off+i].Intensity = 0
}
for i, j := 0.0, off; i < num; i, j = i+1, j+6 {
angle := low + i*delta
sin, cos := math.Sincos(angle)
normalSin, normalCos := pixel.V(sin, cos).ScaledXY(radius).Unit().XY()
a := pt.pos.Add(pixel.V(
radius.X*cos-thickness/2*normalCos,
radius.Y*sin-thickness/2*normalSin,
))
b := pt.pos.Add(pixel.V(
radius.X*cos+thickness/2*normalCos,
radius.Y*sin+thickness/2*normalSin,
))
angle = low + (i+1)*delta
sin, cos = math.Sincos(angle)
normalSin, normalCos = pixel.V(sin, cos).ScaledXY(radius).Unit().XY()
c := pt.pos.Add(pixel.V(
radius.X*cos-thickness/2*normalCos,
radius.Y*sin-thickness/2*normalSin,
))
d := pt.pos.Add(pixel.V(
radius.X*cos+thickness/2*normalCos,
radius.Y*sin+thickness/2*normalSin,
))
(*imd.tri)[j+0].Position = a
(*imd.tri)[j+1].Position = b
(*imd.tri)[j+2].Position = c
(*imd.tri)[j+3].Position = c
(*imd.tri)[j+4].Position = b
(*imd.tri)[j+5].Position = d
}
imd.applyMatrixAndMask(off)
imd.batch.Dirty()
if doEndShape {
lowSin, lowCos := math.Sincos(low)
lowCenter := pt.pos.Add(pixel.V(
radius.X*lowCos,
radius.Y*lowSin,
))
normalLowSin, normalLowCos := pixel.V(lowSin, lowCos).ScaledXY(radius).Unit().XY()
normalLow := pixel.V(normalLowCos, normalLowSin).Angle()
highSin, highCos := math.Sincos(high)
highCenter := pt.pos.Add(pixel.V(
radius.X*highCos,
radius.Y*highSin,
))
normalHighSin, normalHighCos := pixel.V(highSin, highCos).ScaledXY(radius).Unit().XY()
normalHigh := pixel.V(normalHighCos, normalHighSin).Angle()
orientation := 1.0
if low > high {
orientation = -1.0
}
switch pt.endshape {
case NoEndShape:
// nothing
case SharpEndShape:
thick := pixel.V(thickness/2, 0).Rotated(normalLow)
imd.pushPt(lowCenter.Add(thick), pt)
imd.pushPt(lowCenter.Sub(thick), pt)
imd.pushPt(lowCenter.Sub(thick.Normal().Scaled(orientation)), pt)
imd.fillPolygon()
thick = pixel.V(thickness/2, 0).Rotated(normalHigh)
imd.pushPt(highCenter.Add(thick), pt)
imd.pushPt(highCenter.Sub(thick), pt)
imd.pushPt(highCenter.Add(thick.Normal().Scaled(orientation)), pt)
imd.fillPolygon()
case RoundEndShape:
imd.pushPt(lowCenter, pt)
imd.fillEllipseArc(pixel.V(thickness/2, thickness/2), normalLow, normalLow-math.Pi*orientation)
imd.pushPt(highCenter, pt)
imd.fillEllipseArc(pixel.V(thickness/2, thickness/2), normalHigh, normalHigh+math.Pi*orientation)
}
}
}
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 {
// one point special case
points = append(points, points[0])
}
// first point
j, i := 0, 1
ijNormal := points[0].pos.To(points[1].pos).Normal().Unit().Scaled(thickness / 2)
if !closed {
switch points[j].endshape {
case NoEndShape:
// nothing
case SharpEndShape:
imd.pushPt(points[j].pos.Add(ijNormal), points[j])
imd.pushPt(points[j].pos.Sub(ijNormal), points[j])
imd.pushPt(points[j].pos.Add(ijNormal.Normal()), points[j])
imd.fillPolygon()
case RoundEndShape:
imd.pushPt(points[j].pos, points[j])
imd.fillEllipseArc(pixel.V(thickness/2, thickness/2), ijNormal.Angle(), ijNormal.Angle()+math.Pi)
}
}
imd.pushPt(points[j].pos.Add(ijNormal), points[j])
imd.pushPt(points[j].pos.Sub(ijNormal), points[j])
// middle points
for i := 0; i < len(points); i++ {
j, k := i+1, i+2
closing := false
if j >= len(points) {
j %= len(points)
closing = true
}
if k >= len(points) {
if !closed {
break
}
k %= len(points)
}
jkNormal := points[j].pos.To(points[k].pos).Normal().Unit().Scaled(thickness / 2)
orientation := 1.0
if ijNormal.Cross(jkNormal) > 0 {
orientation = -1.0
}
imd.pushPt(points[j].pos.Sub(ijNormal), points[j])
imd.pushPt(points[j].pos.Add(ijNormal), points[j])
imd.fillPolygon()
switch points[j].endshape {
case NoEndShape:
// nothing
case SharpEndShape:
imd.pushPt(points[j].pos, points[j])
imd.pushPt(points[j].pos.Add(ijNormal.Scaled(orientation)), points[j])
imd.pushPt(points[j].pos.Add(jkNormal.Scaled(orientation)), points[j])
imd.fillPolygon()
case RoundEndShape:
imd.pushPt(points[j].pos, points[j])
imd.fillEllipseArc(pixel.V(thickness/2, thickness/2), ijNormal.Angle(), ijNormal.Angle()-math.Pi)
imd.pushPt(points[j].pos, points[j])
imd.fillEllipseArc(pixel.V(thickness/2, thickness/2), jkNormal.Angle(), jkNormal.Angle()+math.Pi)
}
if !closing {
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
i, j = len(points)-2, len(points)-1
ijNormal = points[i].pos.To(points[j].pos).Normal().Unit().Scaled(thickness / 2)
imd.pushPt(points[j].pos.Sub(ijNormal), points[j])
imd.pushPt(points[j].pos.Add(ijNormal), points[j])
imd.fillPolygon()
if !closed {
switch points[j].endshape {
case NoEndShape:
// nothing
case SharpEndShape:
imd.pushPt(points[j].pos.Add(ijNormal), points[j])
imd.pushPt(points[j].pos.Sub(ijNormal), points[j])
imd.pushPt(points[j].pos.Add(ijNormal.Normal().Scaled(-1)), points[j])
imd.fillPolygon()
case RoundEndShape:
imd.pushPt(points[j].pos, points[j])
imd.fillEllipseArc(pixel.V(thickness/2, thickness/2), ijNormal.Angle(), ijNormal.Angle()-math.Pi)
}
}
imd.restorePoints(points)
}

133
vendor/github.com/faiface/pixel/interface.go generated vendored Normal file
View File

@@ -0,0 +1,133 @@
package pixel
import "image/color"
// Target is something that can be drawn onto, such as a window, a canvas, and so on.
//
// You can notice, that there are no "drawing" methods in a Target. That's because all drawing
// happens indirectly through Triangles and Picture instances generated via MakeTriangles and
// MakePicture method.
type Target interface {
// MakeTriangles generates a specialized copy of the provided Triangles.
//
// When calling Draw method on the returned TargetTriangles, the TargetTriangles will be
// drawn onto the Target that generated them.
//
// Note, that not every Target has to recognize all possible types of Triangles. Some may
// only recognize TrianglesPosition and TrianglesColor and ignore all other properties (if
// present) when making new TargetTriangles. This varies from Target to Target.
MakeTriangles(Triangles) TargetTriangles
// MakePicture generates a specialized copy of the provided Picture.
//
// When calling Draw method on the returned TargetPicture, the TargetPicture will be drawn
// onto the Target that generated it together with the TargetTriangles supplied to the Draw
// method.
MakePicture(Picture) TargetPicture
}
// BasicTarget is a Target with additional basic adjustment methods.
type BasicTarget interface {
Target
// SetMatrix sets a Matrix that every point will be projected by.
SetMatrix(Matrix)
// SetColorMask sets a color that will be multiplied with the TrianglesColor property of all
// Triangles.
SetColorMask(color.Color)
}
// Triangles represents a list of vertices, where each three vertices form a triangle. (First,
// second and third is the first triangle, fourth, fifth and sixth is the second triangle, etc.)
type Triangles interface {
// Len returns the number of vertices. The number of triangles is the number of vertices
// divided by 3.
Len() int
// SetLen resizes Triangles to len vertices. If Triangles B were obtained by calling Slice
// method on Triangles A, the relationship between A and B is undefined after calling SetLen
// on either one of them.
SetLen(len int)
// Slice returns a sub-Triangles of this Triangles, covering vertices in range [i, j).
//
// If Triangles B were obtained by calling Slice(4, 9) on Triangles A, then A and B must
// share the same underlying data. Modifying B must change the contents of A in range
// [4, 9). The vertex with index 0 at B is the vertex with index 4 in A, and so on.
//
// Returned Triangles must have the same underlying type.
Slice(i, j int) Triangles
// Update copies vertex properties from the supplied Triangles into this Triangles.
//
// Properies not supported by these Triangles should be ignored. Properties not supported by
// the supplied Triangles should be left untouched.
//
// The two Triangles must have the same Len.
Update(Triangles)
// Copy creates an exact independent copy of this Triangles (with the same underlying type).
Copy() Triangles
}
// TargetTriangles are Triangles generated by a Target with MakeTriangles method. They can be drawn
// onto that (no other) Target.
type TargetTriangles interface {
Triangles
// Draw draws Triangles onto an associated Target.
Draw()
}
// TrianglesPosition specifies Triangles with Position property.
type TrianglesPosition interface {
Triangles
Position(i int) Vec
}
// TrianglesColor specifies Triangles with Color property.
type TrianglesColor interface {
Triangles
Color(i int) RGBA
}
// TrianglesPicture specifies Triangles with Picture propery.
//
// The first value returned from Picture method is Picture coordinates. The second one specifies the
// weight of the Picture. Value of 0 means, that Picture should be completely ignored, 1 means that
// is should be fully included and anything in between means anything in between.
type TrianglesPicture interface {
Triangles
Picture(i int) (pic Vec, intensity float64)
}
// Picture represents a rectangular area of raster data, such as a color. It has Bounds which
// specify the rectangle where data is located.
type Picture interface {
// Bounds returns the rectangle of the Picture. All data is located witih this rectangle.
// Querying properties outside the rectangle should return default value of that property.
Bounds() Rect
}
// TargetPicture is a Picture generated by a Target using MakePicture method. This Picture can be drawn onto
// that (no other) Target together with a TargetTriangles generated by the same Target.
//
// The TargetTriangles specify where, shape and how the Picture should be drawn.
type TargetPicture interface {
Picture
// Draw draws the supplied TargetTriangles (which must be generated by the same Target as
// this TargetPicture) with this TargetPicture. The TargetTriangles should utilize the data
// from this TargetPicture in some way.
Draw(TargetTriangles)
}
// PictureColor specifies Picture with Color property, so that every position inside the Picture's
// Bounds has a color.
//
// Positions outside the Picture's Bounds must return full transparent (Alpha(0)).
type PictureColor interface {
Picture
Color(at Vec) RGBA
}

431
vendor/github.com/faiface/pixel/pixelgl/canvas.go generated vendored Normal file
View File

@@ -0,0 +1,431 @@
package pixelgl
import (
"fmt"
"image/color"
"github.com/faiface/glhf"
"github.com/faiface/mainthread"
"github.com/faiface/pixel"
"github.com/go-gl/mathgl/mgl32"
"github.com/pkg/errors"
)
// Canvas is an off-screen rectangular BasicTarget and Picture at the same time, that you can draw
// onto.
//
// It supports TrianglesPosition, TrianglesColor, TrianglesPicture and PictureColor.
type Canvas struct {
gf *GLFrame
shader *glhf.Shader
cmp pixel.ComposeMethod
mat mgl32.Mat3
col mgl32.Vec4
smooth bool
sprite *pixel.Sprite
}
var _ pixel.ComposeTarget = (*Canvas)(nil)
// NewCanvas creates a new empty, fully transparent Canvas with given bounds.
func NewCanvas(bounds pixel.Rect) *Canvas {
c := &Canvas{
gf: NewGLFrame(bounds),
mat: mgl32.Ident3(),
col: mgl32.Vec4{1, 1, 1, 1},
}
c.SetBounds(bounds)
var shader *glhf.Shader
mainthread.Call(func() {
var err error
shader, err = glhf.NewShader(
canvasVertexFormat,
canvasUniformFormat,
canvasVertexShader,
canvasFragmentShader,
)
if err != nil {
panic(errors.Wrap(err, "failed to create Canvas, there's a bug in the shader"))
}
})
c.shader = shader
return c
}
// MakeTriangles creates a specialized copy of the supplied Triangles that draws onto this Canvas.
//
// TrianglesPosition, TrianglesColor and TrianglesPicture are supported.
func (c *Canvas) MakeTriangles(t pixel.Triangles) pixel.TargetTriangles {
return &canvasTriangles{
GLTriangles: NewGLTriangles(c.shader, t),
dst: c,
}
}
// MakePicture create a specialized copy of the supplied Picture that draws onto this Canvas.
//
// PictureColor is supported.
func (c *Canvas) MakePicture(p pixel.Picture) pixel.TargetPicture {
if cp, ok := p.(*canvasPicture); ok {
return &canvasPicture{
GLPicture: cp.GLPicture,
dst: c,
}
}
if gp, ok := p.(GLPicture); ok {
return &canvasPicture{
GLPicture: gp,
dst: c,
}
}
return &canvasPicture{
GLPicture: NewGLPicture(p),
dst: c,
}
}
// SetMatrix sets a Matrix that every point will be projected by.
func (c *Canvas) SetMatrix(m pixel.Matrix) {
// 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])
}
}
// SetColorMask sets a color that every color in triangles or a picture will be multiplied by.
func (c *Canvas) SetColorMask(col color.Color) {
rgba := pixel.Alpha(1)
if col != nil {
rgba = pixel.ToRGBA(col)
}
c.col = mgl32.Vec4{
float32(rgba.R),
float32(rgba.G),
float32(rgba.B),
float32(rgba.A),
}
}
// SetComposeMethod sets a Porter-Duff composition method to be used in the following draws onto
// this Canvas.
func (c *Canvas) SetComposeMethod(cmp pixel.ComposeMethod) {
c.cmp = cmp
}
// SetBounds resizes the Canvas to the new bounds. Old content will be preserved.
func (c *Canvas) SetBounds(bounds pixel.Rect) {
c.gf.SetBounds(bounds)
if c.sprite == nil {
c.sprite = pixel.NewSprite(nil, pixel.Rect{})
}
c.sprite.Set(c, c.Bounds())
//c.sprite.SetMatrix(pixel.IM.Moved(c.Bounds().Center()))
}
// Bounds returns the rectangular bounds of the Canvas.
func (c *Canvas) Bounds() pixel.Rect {
return c.gf.Bounds()
}
// SetSmooth sets whether stretched Pictures drawn onto this Canvas should be drawn smooth or
// pixely.
func (c *Canvas) SetSmooth(smooth bool) {
c.smooth = smooth
}
// Smooth returns whether stretched Pictures drawn onto this Canvas are set to be drawn smooth or
// pixely.
func (c *Canvas) Smooth() bool {
return c.smooth
}
// must be manually called inside mainthread
func (c *Canvas) setGlhfBounds() {
_, _, bw, bh := intBounds(c.gf.Bounds())
glhf.Bounds(0, 0, bw, bh)
}
// must be manually called inside mainthread
func setBlendFunc(cmp pixel.ComposeMethod) {
switch cmp {
case pixel.ComposeOver:
glhf.BlendFunc(glhf.One, glhf.OneMinusSrcAlpha)
case pixel.ComposeIn:
glhf.BlendFunc(glhf.DstAlpha, glhf.Zero)
case pixel.ComposeOut:
glhf.BlendFunc(glhf.OneMinusDstAlpha, glhf.Zero)
case pixel.ComposeAtop:
glhf.BlendFunc(glhf.DstAlpha, glhf.OneMinusSrcAlpha)
case pixel.ComposeRover:
glhf.BlendFunc(glhf.OneMinusDstAlpha, glhf.One)
case pixel.ComposeRin:
glhf.BlendFunc(glhf.Zero, glhf.SrcAlpha)
case pixel.ComposeRout:
glhf.BlendFunc(glhf.Zero, glhf.OneMinusSrcAlpha)
case pixel.ComposeRatop:
glhf.BlendFunc(glhf.OneMinusDstAlpha, glhf.SrcAlpha)
case pixel.ComposeXor:
glhf.BlendFunc(glhf.OneMinusDstAlpha, glhf.OneMinusSrcAlpha)
case pixel.ComposePlus:
glhf.BlendFunc(glhf.One, glhf.One)
case pixel.ComposeCopy:
glhf.BlendFunc(glhf.One, glhf.Zero)
default:
panic(errors.New("Canvas: invalid compose method"))
}
}
// Clear fills the whole Canvas with a single color.
func (c *Canvas) Clear(color color.Color) {
c.gf.Dirty()
rgba := pixel.ToRGBA(color)
// color masking
rgba = rgba.Mul(pixel.RGBA{
R: float64(c.col[0]),
G: float64(c.col[1]),
B: float64(c.col[2]),
A: float64(c.col[3]),
})
mainthread.CallNonBlock(func() {
c.setGlhfBounds()
c.gf.Frame().Begin()
glhf.Clear(
float32(rgba.R),
float32(rgba.G),
float32(rgba.B),
float32(rgba.A),
)
c.gf.Frame().End()
})
}
// Color returns the color of the pixel over the given position inside the Canvas.
func (c *Canvas) Color(at pixel.Vec) pixel.RGBA {
return c.gf.Color(at)
}
// Texture returns the underlying OpenGL Texture of this Canvas.
//
// Implements GLPicture interface.
func (c *Canvas) Texture() *glhf.Texture {
return c.gf.Texture()
}
// Frame returns 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) {
c.gf.Dirty()
mainthread.Call(func() {
tex := c.Texture()
tex.Begin()
tex.SetPixels(0, 0, tex.Width(), tex.Height(), pixels)
tex.End()
})
}
// Pixels returns an alpha-premultiplied RGBA sequence of the content of the Canvas.
func (c *Canvas) Pixels() []uint8 {
var pixels []uint8
mainthread.Call(func() {
tex := c.Texture()
tex.Begin()
pixels = tex.Pixels(0, 0, tex.Width(), tex.Height())
tex.End()
})
return pixels
}
// Draw draws the content of the Canvas onto another Target, transformed by the given Matrix, just
// like if it was a Sprite containing the whole Canvas.
func (c *Canvas) Draw(t pixel.Target, matrix pixel.Matrix) {
c.sprite.Draw(t, matrix)
}
// DrawColorMask draws the content of the Canvas onto another Target, transformed by the given
// Matrix and multiplied by the given mask, just like if it was a Sprite containing the whole Canvas.
//
// If the color mask is nil, a fully opaque white mask will be used causing no effect.
func (c *Canvas) DrawColorMask(t pixel.Target, matrix pixel.Matrix, mask color.Color) {
c.sprite.DrawColorMask(t, matrix, mask)
}
type canvasTriangles struct {
*GLTriangles
dst *Canvas
}
func (ct *canvasTriangles) draw(tex *glhf.Texture, bounds pixel.Rect) {
ct.dst.gf.Dirty()
// save the current state vars to avoid race condition
cmp := ct.dst.cmp
mat := ct.dst.mat
col := ct.dst.col
smt := ct.dst.smooth
mainthread.CallNonBlock(func() {
ct.dst.setGlhfBounds()
setBlendFunc(cmp)
frame := ct.dst.gf.Frame()
shader := ct.dst.shader
frame.Begin()
shader.Begin()
dstBounds := ct.dst.Bounds()
shader.SetUniformAttr(canvasBounds, mgl32.Vec4{
float32(dstBounds.Min.X),
float32(dstBounds.Min.Y),
float32(dstBounds.W()),
float32(dstBounds.H()),
})
shader.SetUniformAttr(canvasTransform, mat)
shader.SetUniformAttr(canvasColorMask, col)
if tex == nil {
ct.vs.Begin()
ct.vs.Draw()
ct.vs.End()
} else {
tex.Begin()
bx, by, bw, bh := intBounds(bounds)
shader.SetUniformAttr(canvasTexBounds, mgl32.Vec4{
float32(bx),
float32(by),
float32(bw),
float32(bh),
})
if tex.Smooth() != smt {
tex.SetSmooth(smt)
}
ct.vs.Begin()
ct.vs.Draw()
ct.vs.End()
tex.End()
}
shader.End()
frame.End()
})
}
func (ct *canvasTriangles) Draw() {
ct.draw(nil, pixel.Rect{})
}
type canvasPicture struct {
GLPicture
dst *Canvas
}
func (cp *canvasPicture) Draw(t pixel.TargetTriangles) {
ct := t.(*canvasTriangles)
if cp.dst != ct.dst {
panic(fmt.Errorf("(%T).Draw: TargetTriangles generated by different Canvas", cp))
}
ct.draw(cp.GLPicture.Texture(), cp.GLPicture.Bounds())
}
const (
canvasPosition int = iota
canvasColor
canvasTexCoords
canvasIntensity
)
var canvasVertexFormat = glhf.AttrFormat{
canvasPosition: {Name: "position", Type: glhf.Vec2},
canvasColor: {Name: "color", Type: glhf.Vec4},
canvasTexCoords: {Name: "texCoords", Type: glhf.Vec2},
canvasIntensity: {Name: "intensity", Type: glhf.Float},
}
const (
canvasTransform int = iota
canvasColorMask
canvasBounds
canvasTexBounds
)
var canvasUniformFormat = glhf.AttrFormat{
canvasTransform: {Name: "transform", Type: glhf.Mat3},
canvasColorMask: {Name: "colorMask", Type: glhf.Vec4},
canvasBounds: {Name: "bounds", Type: glhf.Vec4},
canvasTexBounds: {Name: "texBounds", Type: glhf.Vec4},
}
var canvasVertexShader = `
#version 330 core
in vec2 position;
in vec4 color;
in vec2 texCoords;
in float intensity;
out vec4 Color;
out vec2 TexCoords;
out float Intensity;
uniform mat3 transform;
uniform vec4 bounds;
void main() {
vec2 transPos = (transform * vec3(position, 1.0)).xy;
vec2 normPos = (transPos - bounds.xy) / bounds.zw * 2 - vec2(1, 1);
gl_Position = vec4(normPos, 0.0, 1.0);
Color = color;
TexCoords = texCoords;
Intensity = intensity;
}
`
var canvasFragmentShader = `
#version 330 core
in vec4 Color;
in vec2 TexCoords;
in float Intensity;
out vec4 color;
uniform vec4 colorMask;
uniform vec4 texBounds;
uniform sampler2D tex;
void main() {
if (Intensity == 0) {
color = colorMask * Color;
} else {
color = vec4(0, 0, 0, 0);
color += (1 - Intensity) * Color;
vec2 t = (TexCoords - texBounds.xy) / texBounds.zw;
color += Intensity * Color * texture(tex, t);
color *= colorMask;
}
}
`

5
vendor/github.com/faiface/pixel/pixelgl/doc.go generated vendored Normal file
View File

@@ -0,0 +1,5 @@
// Package pixelgl implements efficient OpenGL targets and utilities for the Pixel game development
// library, specifically Window and Canvas.
//
// It also contains a few additional utilities to help extend Pixel with OpenGL graphical effects.
package pixelgl

105
vendor/github.com/faiface/pixel/pixelgl/glframe.go generated vendored Normal file
View File

@@ -0,0 +1,105 @@
package pixelgl
import (
"github.com/faiface/glhf"
"github.com/faiface/mainthread"
"github.com/faiface/pixel"
)
// GLFrame is a type that helps implementing OpenGL Targets. It implements most common methods to
// avoid code redundancy. It contains an glhf.Frame that you can draw on.
type GLFrame struct {
frame *glhf.Frame
bounds pixel.Rect
pixels []uint8
dirty bool
}
// NewGLFrame creates a new GLFrame with the given bounds.
func NewGLFrame(bounds pixel.Rect) *GLFrame {
gf := new(GLFrame)
gf.SetBounds(bounds)
return gf
}
// SetBounds resizes the GLFrame to the new bounds.
func (gf *GLFrame) SetBounds(bounds pixel.Rect) {
if bounds == gf.Bounds() {
return
}
mainthread.Call(func() {
oldF := gf.frame
_, _, w, h := intBounds(bounds)
if w <= 0 {
w = 1
}
if h <= 0 {
h = 1
}
gf.frame = glhf.NewFrame(w, h, false)
// preserve old content
if oldF != nil {
ox, oy, ow, oh := intBounds(bounds)
oldF.Blit(
gf.frame,
ox, oy, ox+ow, oy+oh,
ox, oy, ox+ow, oy+oh,
)
}
})
gf.bounds = bounds
gf.pixels = nil
gf.dirty = true
}
// Bounds returns the current GLFrame's bounds.
func (gf *GLFrame) Bounds() pixel.Rect {
return gf.bounds
}
// Color returns the color of the pixel under the specified position.
func (gf *GLFrame) Color(at pixel.Vec) pixel.RGBA {
if gf.dirty {
mainthread.Call(func() {
tex := gf.frame.Texture()
tex.Begin()
gf.pixels = tex.Pixels(0, 0, tex.Width(), tex.Height())
tex.End()
})
gf.dirty = false
}
if !gf.bounds.Contains(at) {
return pixel.Alpha(0)
}
bx, by, bw, _ := intBounds(gf.bounds)
x, y := int(at.X)-bx, int(at.Y)-by
off := y*bw + x
return pixel.RGBA{
R: float64(gf.pixels[off*4+0]) / 255,
G: float64(gf.pixels[off*4+1]) / 255,
B: float64(gf.pixels[off*4+2]) / 255,
A: float64(gf.pixels[off*4+3]) / 255,
}
}
// Frame returns the GLFrame's Frame that you can draw on.
func (gf *GLFrame) Frame() *glhf.Frame {
return gf.frame
}
// Texture returns the underlying Texture of the GLFrame's Frame.
//
// Implements GLPicture interface.
func (gf *GLFrame) Texture() *glhf.Texture {
return gf.frame.Texture()
}
// Dirty marks the GLFrame as changed. Always call this method when you draw onto the GLFrame's
// Frame.
func (gf *GLFrame) Dirty() {
gf.dirty = true
}

98
vendor/github.com/faiface/pixel/pixelgl/glpicture.go generated vendored Normal file
View File

@@ -0,0 +1,98 @@
package pixelgl
import (
"math"
"github.com/faiface/glhf"
"github.com/faiface/mainthread"
"github.com/faiface/pixel"
)
// GLPicture is a pixel.PictureColor with a Texture. All OpenGL Targets should implement and accept
// this interface, because it enables seamless drawing of one to another.
//
// Implementing this interface on an OpenGL Target enables other OpenGL Targets to efficiently draw
// that Target onto them.
type GLPicture interface {
pixel.PictureColor
Texture() *glhf.Texture
}
// NewGLPicture creates a new GLPicture with it's own static OpenGL texture. This function always
// allocates a new texture that cannot (shouldn't) be further modified.
func NewGLPicture(p pixel.Picture) GLPicture {
bounds := p.Bounds()
bx, by, bw, bh := intBounds(bounds)
pixels := make([]uint8, 4*bw*bh)
if pd, ok := p.(*pixel.PictureData); ok {
// PictureData short path
for y := 0; y < bh; y++ {
for x := 0; x < bw; x++ {
rgba := pd.Pix[y*pd.Stride+x]
off := (y*bw + x) * 4
pixels[off+0] = rgba.R
pixels[off+1] = rgba.G
pixels[off+2] = rgba.B
pixels[off+3] = rgba.A
}
}
} else if p, ok := p.(pixel.PictureColor); ok {
for y := 0; y < bh; y++ {
for x := 0; x < bw; x++ {
at := pixel.V(
math.Max(float64(bx+x), bounds.Min.X),
math.Max(float64(by+y), bounds.Min.Y),
)
color := p.Color(at)
off := (y*bw + x) * 4
pixels[off+0] = uint8(color.R * 255)
pixels[off+1] = uint8(color.G * 255)
pixels[off+2] = uint8(color.B * 255)
pixels[off+3] = uint8(color.A * 255)
}
}
}
var tex *glhf.Texture
mainthread.Call(func() {
tex = glhf.NewTexture(bw, bh, false, pixels)
})
gp := &glPicture{
bounds: bounds,
tex: tex,
pixels: pixels,
}
return gp
}
type glPicture struct {
bounds pixel.Rect
tex *glhf.Texture
pixels []uint8
}
func (gp *glPicture) Bounds() pixel.Rect {
return gp.bounds
}
func (gp *glPicture) Texture() *glhf.Texture {
return gp.tex
}
func (gp *glPicture) Color(at pixel.Vec) pixel.RGBA {
if !gp.bounds.Contains(at) {
return pixel.Alpha(0)
}
bx, by, bw, _ := intBounds(gp.bounds)
x, y := int(at.X)-bx, int(at.Y)-by
off := y*bw + x
return pixel.RGBA{
R: float64(gp.pixels[off*4+0]) / 255,
G: float64(gp.pixels[off*4+1]) / 255,
B: float64(gp.pixels[off*4+2]) / 255,
A: float64(gp.pixels[off*4+3]) / 255,
}
}

215
vendor/github.com/faiface/pixel/pixelgl/gltriangles.go generated vendored Normal file
View File

@@ -0,0 +1,215 @@
package pixelgl
import (
"fmt"
"github.com/faiface/glhf"
"github.com/faiface/mainthread"
"github.com/faiface/pixel"
)
// GLTriangles are OpenGL triangles implemented using glhf.VertexSlice.
//
// Triangles returned from this function support TrianglesPosition, TrianglesColor and
// TrianglesPicture. If you need to support more, you can "override" SetLen and Update methods.
type GLTriangles struct {
vs *glhf.VertexSlice
data []float32
shader *glhf.Shader
}
var (
_ pixel.TrianglesPosition = (*GLTriangles)(nil)
_ pixel.TrianglesColor = (*GLTriangles)(nil)
_ pixel.TrianglesPicture = (*GLTriangles)(nil)
)
// NewGLTriangles returns GLTriangles initialized with the data from the supplied Triangles.
//
// Only draw the Triangles using the provided Shader.
func NewGLTriangles(shader *glhf.Shader, t pixel.Triangles) *GLTriangles {
var gt *GLTriangles
mainthread.Call(func() {
gt = &GLTriangles{
vs: glhf.MakeVertexSlice(shader, 0, t.Len()),
shader: shader,
}
})
gt.SetLen(t.Len())
gt.Update(t)
return gt
}
// VertexSlice returns the VertexSlice of this GLTriangles.
//
// You can use it to draw them.
func (gt *GLTriangles) VertexSlice() *glhf.VertexSlice {
return gt.vs
}
// Shader returns the GLTriangles's associated shader.
func (gt *GLTriangles) Shader() *glhf.Shader {
return gt.shader
}
// Len returns the number of vertices.
func (gt *GLTriangles) Len() int {
return len(gt.data) / gt.vs.Stride()
}
// SetLen efficiently resizes GLTriangles to len.
//
// Time complexity is amortized O(1).
func (gt *GLTriangles) SetLen(length int) {
switch {
case length > gt.Len():
needAppend := length - gt.Len()
for i := 0; i < needAppend; i++ {
gt.data = append(gt.data,
0, 0,
1, 1, 1, 1,
0, 0,
0,
)
}
case length < gt.Len():
gt.data = gt.data[:length*gt.vs.Stride()]
default:
return
}
mainthread.CallNonBlock(func() {
gt.vs.Begin()
gt.vs.SetLen(length)
gt.vs.End()
})
}
// Slice returns a sub-Triangles of this GLTriangles in range [i, j).
func (gt *GLTriangles) Slice(i, j int) pixel.Triangles {
return &GLTriangles{
vs: gt.vs.Slice(i, j),
data: gt.data[i*gt.vs.Stride() : j*gt.vs.Stride()],
shader: gt.shader,
}
}
func (gt *GLTriangles) updateData(t pixel.Triangles) {
// glTriangles short path
if t, ok := t.(*GLTriangles); ok {
copy(gt.data, t.data)
return
}
// TrianglesData short path
stride := gt.vs.Stride()
length := gt.Len()
if t, ok := t.(*pixel.TrianglesData); ok {
for i := 0; i < length; i++ {
var (
px, py = (*t)[i].Position.XY()
col = (*t)[i].Color
tx, ty = (*t)[i].Picture.XY()
in = (*t)[i].Intensity
)
d := gt.data[i*stride : i*stride+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
}
if t, ok := t.(pixel.TrianglesPosition); ok {
for i := 0; i < length; i++ {
px, py := t.Position(i).XY()
gt.data[i*stride+0] = float32(px)
gt.data[i*stride+1] = float32(py)
}
}
if t, ok := t.(pixel.TrianglesColor); ok {
for i := 0; i < length; i++ {
col := t.Color(i)
gt.data[i*stride+2] = float32(col.R)
gt.data[i*stride+3] = float32(col.G)
gt.data[i*stride+4] = float32(col.B)
gt.data[i*stride+5] = float32(col.A)
}
}
if t, ok := t.(pixel.TrianglesPicture); ok {
for i := 0; i < length; i++ {
pic, intensity := t.Picture(i)
gt.data[i*stride+6] = float32(pic.X)
gt.data[i*stride+7] = float32(pic.Y)
gt.data[i*stride+8] = float32(intensity)
}
}
}
// Update copies vertex properties from the supplied Triangles into this GLTriangles.
//
// The two Triangles (gt and t) must be of the same len.
func (gt *GLTriangles) Update(t pixel.Triangles) {
if gt.Len() != t.Len() {
panic(fmt.Errorf("(%T).Update: invalid triangles len", gt))
}
gt.updateData(t)
// this code is supposed to copy the vertex data and CallNonBlock the update if
// the data is small enough, otherwise it'll block and not copy the data
if len(gt.data) < 256 { // arbitrary heurestic constant
data := append([]float32{}, gt.data...)
mainthread.CallNonBlock(func() {
gt.vs.Begin()
gt.vs.SetVertexData(data)
gt.vs.End()
})
} else {
mainthread.Call(func() {
gt.vs.Begin()
gt.vs.SetVertexData(gt.data)
gt.vs.End()
})
}
}
// Copy returns an independent copy of this GLTriangles.
//
// The returned Triangles are *GLTriangles as the underlying type.
func (gt *GLTriangles) Copy() pixel.Triangles {
return NewGLTriangles(gt.shader, gt)
}
// Position returns the Position property of the i-th vertex.
func (gt *GLTriangles) Position(i int) pixel.Vec {
px := gt.data[i*gt.vs.Stride()+0]
py := gt.data[i*gt.vs.Stride()+1]
return pixel.V(float64(px), float64(py))
}
// Color returns the Color property of the i-th vertex.
func (gt *GLTriangles) Color(i int) pixel.RGBA {
r := gt.data[i*gt.vs.Stride()+2]
g := gt.data[i*gt.vs.Stride()+3]
b := gt.data[i*gt.vs.Stride()+4]
a := gt.data[i*gt.vs.Stride()+5]
return pixel.RGBA{
R: float64(r),
G: float64(g),
B: float64(b),
A: float64(a),
}
}
// Picture returns the Picture property of the i-th vertex.
func (gt *GLTriangles) Picture(i int) (pic pixel.Vec, intensity float64) {
tx := gt.data[i*gt.vs.Stride()+6]
ty := gt.data[i*gt.vs.Stride()+7]
intensity = float64(gt.data[i*gt.vs.Stride()+8])
return pixel.V(float64(tx), float64(ty)), intensity
}

388
vendor/github.com/faiface/pixel/pixelgl/input.go generated vendored Normal file
View File

@@ -0,0 +1,388 @@
package pixelgl
import (
"github.com/faiface/mainthread"
"github.com/faiface/pixel"
"github.com/go-gl/glfw/v3.2/glfw"
)
// Pressed returns whether the Button is currently pressed down.
func (w *Window) Pressed(button Button) bool {
return w.currInp.buttons[button]
}
// JustPressed returns whether the Button has just been pressed down.
func (w *Window) JustPressed(button Button) bool {
return w.currInp.buttons[button] && !w.prevInp.buttons[button]
}
// JustReleased returns whether the Button has just been released up.
func (w *Window) JustReleased(button Button) bool {
return !w.currInp.buttons[button] && w.prevInp.buttons[button]
}
// Repeated returns whether a repeat event has been triggered on button.
//
// Repeat event occurs repeatedly when a button is held down for some time.
func (w *Window) Repeated(button Button) bool {
return w.currInp.repeat[button]
}
// MousePosition returns the current mouse position in the Window's Bounds.
func (w *Window) MousePosition() pixel.Vec {
return w.currInp.mouse
}
// MouseScroll returns the mouse scroll amount (in both axes) since the last call to Window.Update.
func (w *Window) MouseScroll() pixel.Vec {
return w.currInp.scroll
}
// Typed returns the text typed on the keyboard since the last call to Window.Update.
func (w *Window) Typed() string {
return w.currInp.typed
}
// Button is a keyboard or mouse button. Why distinguish?
type Button int
// List of all mouse buttons.
const (
MouseButton1 = Button(glfw.MouseButton1)
MouseButton2 = Button(glfw.MouseButton2)
MouseButton3 = Button(glfw.MouseButton3)
MouseButton4 = Button(glfw.MouseButton4)
MouseButton5 = Button(glfw.MouseButton5)
MouseButton6 = Button(glfw.MouseButton6)
MouseButton7 = Button(glfw.MouseButton7)
MouseButton8 = Button(glfw.MouseButton8)
MouseButtonLast = Button(glfw.MouseButtonLast)
MouseButtonLeft = Button(glfw.MouseButtonLeft)
MouseButtonRight = Button(glfw.MouseButtonRight)
MouseButtonMiddle = Button(glfw.MouseButtonMiddle)
)
// List of all keyboard buttons.
const (
KeyUnknown = Button(glfw.KeyUnknown)
KeySpace = Button(glfw.KeySpace)
KeyApostrophe = Button(glfw.KeyApostrophe)
KeyComma = Button(glfw.KeyComma)
KeyMinus = Button(glfw.KeyMinus)
KeyPeriod = Button(glfw.KeyPeriod)
KeySlash = Button(glfw.KeySlash)
Key0 = Button(glfw.Key0)
Key1 = Button(glfw.Key1)
Key2 = Button(glfw.Key2)
Key3 = Button(glfw.Key3)
Key4 = Button(glfw.Key4)
Key5 = Button(glfw.Key5)
Key6 = Button(glfw.Key6)
Key7 = Button(glfw.Key7)
Key8 = Button(glfw.Key8)
Key9 = Button(glfw.Key9)
KeySemicolon = Button(glfw.KeySemicolon)
KeyEqual = Button(glfw.KeyEqual)
KeyA = Button(glfw.KeyA)
KeyB = Button(glfw.KeyB)
KeyC = Button(glfw.KeyC)
KeyD = Button(glfw.KeyD)
KeyE = Button(glfw.KeyE)
KeyF = Button(glfw.KeyF)
KeyG = Button(glfw.KeyG)
KeyH = Button(glfw.KeyH)
KeyI = Button(glfw.KeyI)
KeyJ = Button(glfw.KeyJ)
KeyK = Button(glfw.KeyK)
KeyL = Button(glfw.KeyL)
KeyM = Button(glfw.KeyM)
KeyN = Button(glfw.KeyN)
KeyO = Button(glfw.KeyO)
KeyP = Button(glfw.KeyP)
KeyQ = Button(glfw.KeyQ)
KeyR = Button(glfw.KeyR)
KeyS = Button(glfw.KeyS)
KeyT = Button(glfw.KeyT)
KeyU = Button(glfw.KeyU)
KeyV = Button(glfw.KeyV)
KeyW = Button(glfw.KeyW)
KeyX = Button(glfw.KeyX)
KeyY = Button(glfw.KeyY)
KeyZ = Button(glfw.KeyZ)
KeyLeftBracket = Button(glfw.KeyLeftBracket)
KeyBackslash = Button(glfw.KeyBackslash)
KeyRightBracket = Button(glfw.KeyRightBracket)
KeyGraveAccent = Button(glfw.KeyGraveAccent)
KeyWorld1 = Button(glfw.KeyWorld1)
KeyWorld2 = Button(glfw.KeyWorld2)
KeyEscape = Button(glfw.KeyEscape)
KeyEnter = Button(glfw.KeyEnter)
KeyTab = Button(glfw.KeyTab)
KeyBackspace = Button(glfw.KeyBackspace)
KeyInsert = Button(glfw.KeyInsert)
KeyDelete = Button(glfw.KeyDelete)
KeyRight = Button(glfw.KeyRight)
KeyLeft = Button(glfw.KeyLeft)
KeyDown = Button(glfw.KeyDown)
KeyUp = Button(glfw.KeyUp)
KeyPageUp = Button(glfw.KeyPageUp)
KeyPageDown = Button(glfw.KeyPageDown)
KeyHome = Button(glfw.KeyHome)
KeyEnd = Button(glfw.KeyEnd)
KeyCapsLock = Button(glfw.KeyCapsLock)
KeyScrollLock = Button(glfw.KeyScrollLock)
KeyNumLock = Button(glfw.KeyNumLock)
KeyPrintScreen = Button(glfw.KeyPrintScreen)
KeyPause = Button(glfw.KeyPause)
KeyF1 = Button(glfw.KeyF1)
KeyF2 = Button(glfw.KeyF2)
KeyF3 = Button(glfw.KeyF3)
KeyF4 = Button(glfw.KeyF4)
KeyF5 = Button(glfw.KeyF5)
KeyF6 = Button(glfw.KeyF6)
KeyF7 = Button(glfw.KeyF7)
KeyF8 = Button(glfw.KeyF8)
KeyF9 = Button(glfw.KeyF9)
KeyF10 = Button(glfw.KeyF10)
KeyF11 = Button(glfw.KeyF11)
KeyF12 = Button(glfw.KeyF12)
KeyF13 = Button(glfw.KeyF13)
KeyF14 = Button(glfw.KeyF14)
KeyF15 = Button(glfw.KeyF15)
KeyF16 = Button(glfw.KeyF16)
KeyF17 = Button(glfw.KeyF17)
KeyF18 = Button(glfw.KeyF18)
KeyF19 = Button(glfw.KeyF19)
KeyF20 = Button(glfw.KeyF20)
KeyF21 = Button(glfw.KeyF21)
KeyF22 = Button(glfw.KeyF22)
KeyF23 = Button(glfw.KeyF23)
KeyF24 = Button(glfw.KeyF24)
KeyF25 = Button(glfw.KeyF25)
KeyKP0 = Button(glfw.KeyKP0)
KeyKP1 = Button(glfw.KeyKP1)
KeyKP2 = Button(glfw.KeyKP2)
KeyKP3 = Button(glfw.KeyKP3)
KeyKP4 = Button(glfw.KeyKP4)
KeyKP5 = Button(glfw.KeyKP5)
KeyKP6 = Button(glfw.KeyKP6)
KeyKP7 = Button(glfw.KeyKP7)
KeyKP8 = Button(glfw.KeyKP8)
KeyKP9 = Button(glfw.KeyKP9)
KeyKPDecimal = Button(glfw.KeyKPDecimal)
KeyKPDivide = Button(glfw.KeyKPDivide)
KeyKPMultiply = Button(glfw.KeyKPMultiply)
KeyKPSubtract = Button(glfw.KeyKPSubtract)
KeyKPAdd = Button(glfw.KeyKPAdd)
KeyKPEnter = Button(glfw.KeyKPEnter)
KeyKPEqual = Button(glfw.KeyKPEqual)
KeyLeftShift = Button(glfw.KeyLeftShift)
KeyLeftControl = Button(glfw.KeyLeftControl)
KeyLeftAlt = Button(glfw.KeyLeftAlt)
KeyLeftSuper = Button(glfw.KeyLeftSuper)
KeyRightShift = Button(glfw.KeyRightShift)
KeyRightControl = Button(glfw.KeyRightControl)
KeyRightAlt = Button(glfw.KeyRightAlt)
KeyRightSuper = Button(glfw.KeyRightSuper)
KeyMenu = Button(glfw.KeyMenu)
KeyLast = Button(glfw.KeyLast)
)
// String returns a human-readable string describing the Button.
func (b Button) String() string {
name, ok := buttonNames[b]
if !ok {
return "Invalid"
}
return name
}
var buttonNames = map[Button]string{
MouseButton4: "MouseButton4",
MouseButton5: "MouseButton5",
MouseButton6: "MouseButton6",
MouseButton7: "MouseButton7",
MouseButton8: "MouseButton8",
MouseButtonLeft: "MouseButtonLeft",
MouseButtonRight: "MouseButtonRight",
MouseButtonMiddle: "MouseButtonMiddle",
KeyUnknown: "Unknown",
KeySpace: "Space",
KeyApostrophe: "Apostrophe",
KeyComma: "Comma",
KeyMinus: "Minus",
KeyPeriod: "Period",
KeySlash: "Slash",
Key0: "0",
Key1: "1",
Key2: "2",
Key3: "3",
Key4: "4",
Key5: "5",
Key6: "6",
Key7: "7",
Key8: "8",
Key9: "9",
KeySemicolon: "Semicolon",
KeyEqual: "Equal",
KeyA: "A",
KeyB: "B",
KeyC: "C",
KeyD: "D",
KeyE: "E",
KeyF: "F",
KeyG: "G",
KeyH: "H",
KeyI: "I",
KeyJ: "J",
KeyK: "K",
KeyL: "L",
KeyM: "M",
KeyN: "N",
KeyO: "O",
KeyP: "P",
KeyQ: "Q",
KeyR: "R",
KeyS: "S",
KeyT: "T",
KeyU: "U",
KeyV: "V",
KeyW: "W",
KeyX: "X",
KeyY: "Y",
KeyZ: "Z",
KeyLeftBracket: "LeftBracket",
KeyBackslash: "Backslash",
KeyRightBracket: "RightBracket",
KeyGraveAccent: "GraveAccent",
KeyWorld1: "World1",
KeyWorld2: "World2",
KeyEscape: "Escape",
KeyEnter: "Enter",
KeyTab: "Tab",
KeyBackspace: "Backspace",
KeyInsert: "Insert",
KeyDelete: "Delete",
KeyRight: "Right",
KeyLeft: "Left",
KeyDown: "Down",
KeyUp: "Up",
KeyPageUp: "PageUp",
KeyPageDown: "PageDown",
KeyHome: "Home",
KeyEnd: "End",
KeyCapsLock: "CapsLock",
KeyScrollLock: "ScrollLock",
KeyNumLock: "NumLock",
KeyPrintScreen: "PrintScreen",
KeyPause: "Pause",
KeyF1: "F1",
KeyF2: "F2",
KeyF3: "F3",
KeyF4: "F4",
KeyF5: "F5",
KeyF6: "F6",
KeyF7: "F7",
KeyF8: "F8",
KeyF9: "F9",
KeyF10: "F10",
KeyF11: "F11",
KeyF12: "F12",
KeyF13: "F13",
KeyF14: "F14",
KeyF15: "F15",
KeyF16: "F16",
KeyF17: "F17",
KeyF18: "F18",
KeyF19: "F19",
KeyF20: "F20",
KeyF21: "F21",
KeyF22: "F22",
KeyF23: "F23",
KeyF24: "F24",
KeyF25: "F25",
KeyKP0: "KP0",
KeyKP1: "KP1",
KeyKP2: "KP2",
KeyKP3: "KP3",
KeyKP4: "KP4",
KeyKP5: "KP5",
KeyKP6: "KP6",
KeyKP7: "KP7",
KeyKP8: "KP8",
KeyKP9: "KP9",
KeyKPDecimal: "KPDecimal",
KeyKPDivide: "KPDivide",
KeyKPMultiply: "KPMultiply",
KeyKPSubtract: "KPSubtract",
KeyKPAdd: "KPAdd",
KeyKPEnter: "KPEnter",
KeyKPEqual: "KPEqual",
KeyLeftShift: "LeftShift",
KeyLeftControl: "LeftControl",
KeyLeftAlt: "LeftAlt",
KeyLeftSuper: "LeftSuper",
KeyRightShift: "RightShift",
KeyRightControl: "RightControl",
KeyRightAlt: "RightAlt",
KeyRightSuper: "RightSuper",
KeyMenu: "Menu",
}
func (w *Window) initInput() {
mainthread.Call(func() {
w.window.SetMouseButtonCallback(func(_ *glfw.Window, button glfw.MouseButton, action glfw.Action, mod glfw.ModifierKey) {
switch action {
case glfw.Press:
w.tempInp.buttons[Button(button)] = true
case glfw.Release:
w.tempInp.buttons[Button(button)] = false
}
})
w.window.SetKeyCallback(func(_ *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
if key == glfw.KeyUnknown {
return
}
switch action {
case glfw.Press:
w.tempInp.buttons[Button(key)] = true
case glfw.Release:
w.tempInp.buttons[Button(key)] = false
case glfw.Repeat:
w.tempInp.repeat[Button(key)] = true
}
})
w.window.SetCursorPosCallback(func(_ *glfw.Window, x, y float64) {
w.tempInp.mouse = pixel.V(
x+w.bounds.Min.X,
(w.bounds.H()-y)+w.bounds.Min.Y,
)
})
w.window.SetScrollCallback(func(_ *glfw.Window, xoff, yoff float64) {
w.tempInp.scroll.X += xoff
w.tempInp.scroll.Y += yoff
})
w.window.SetCharCallback(func(_ *glfw.Window, r rune) {
w.tempInp.typed += string(r)
})
})
}
// UpdateInput polls window events. Call this function to poll window events
// without swapping buffers. Note that the Update method invokes UpdateInput.
func (w *Window) UpdateInput() {
mainthread.Call(func() {
glfw.PollEvents()
})
w.prevInp = w.currInp
w.currInp = w.tempInp
w.tempInp.repeat = [KeyLast + 1]bool{}
w.tempInp.scroll = pixel.ZV
w.tempInp.typed = ""
}

97
vendor/github.com/faiface/pixel/pixelgl/monitor.go generated vendored Normal file
View File

@@ -0,0 +1,97 @@
package pixelgl
import (
"github.com/faiface/mainthread"
"github.com/go-gl/glfw/v3.2/glfw"
)
// Monitor represents a physical display attached to your computer.
type Monitor struct {
monitor *glfw.Monitor
}
// PrimaryMonitor returns the main monitor (usually the one with the taskbar and stuff).
func PrimaryMonitor() *Monitor {
var monitor *glfw.Monitor
mainthread.Call(func() {
monitor = glfw.GetPrimaryMonitor()
})
return &Monitor{
monitor: monitor,
}
}
// Monitors returns a slice of all currently available monitors.
func Monitors() []*Monitor {
var monitors []*Monitor
mainthread.Call(func() {
for _, monitor := range glfw.GetMonitors() {
monitors = append(monitors, &Monitor{monitor: monitor})
}
})
return monitors
}
// Name returns a human-readable name of the Monitor.
func (m *Monitor) Name() string {
var name string
mainthread.Call(func() {
name = m.monitor.GetName()
})
return name
}
// PhysicalSize returns the size of the display area of the Monitor in millimeters.
func (m *Monitor) PhysicalSize() (width, height float64) {
var wi, hi int
mainthread.Call(func() {
wi, hi = m.monitor.GetPhysicalSize()
})
width = float64(wi)
height = float64(hi)
return
}
// Position returns the position of the upper-left corner of the Monitor in screen coordinates.
func (m *Monitor) Position() (x, y float64) {
var xi, yi int
mainthread.Call(func() {
xi, yi = m.monitor.GetPos()
})
x = float64(xi)
y = float64(yi)
return
}
// Size returns the resolution of the Monitor in pixels.
func (m *Monitor) Size() (width, height float64) {
var mode *glfw.VidMode
mainthread.Call(func() {
mode = m.monitor.GetVideoMode()
})
width = float64(mode.Width)
height = float64(mode.Height)
return
}
// BitDepth returns the number of bits per color of the Monitor.
func (m *Monitor) BitDepth() (red, green, blue int) {
var mode *glfw.VidMode
mainthread.Call(func() {
mode = m.monitor.GetVideoMode()
})
red = mode.RedBits
green = mode.GreenBits
blue = mode.BlueBits
return
}
// RefreshRate returns the refresh frequency of the Monitor in Hz (refreshes/second).
func (m *Monitor) RefreshRate() (rate float64) {
var mode *glfw.VidMode
mainthread.Call(func() {
mode = m.monitor.GetVideoMode()
})
rate = float64(mode.RefreshRate)
return
}

33
vendor/github.com/faiface/pixel/pixelgl/run.go generated vendored Normal file
View File

@@ -0,0 +1,33 @@
package pixelgl
import (
"github.com/faiface/mainthread"
"github.com/go-gl/glfw/v3.2/glfw"
"github.com/pkg/errors"
)
// Run is essentially the main function of PixelGL. It exists mainly due to the technical
// limitations of OpenGL and operating systems. In short, all graphics and window manipulating calls
// must be done from the main thread. Run makes this possible.
//
// Call this function from the main function of your application. This is necessary, so that Run
// runs on the main thread.
//
// func run() {
// // interact with Pixel and PixelGL from here (even concurrently)
// }
//
// func main() {
// pixel.Run(run)
// }
//
// You can spawn any number of goroutines from your run function and interact with PixelGL
// concurrently. The only condition is that the Run function is called from your main function.
func Run(run func()) {
err := glfw.Init()
if err != nil {
panic(errors.Wrap(err, "failed to initialize GLFW"))
}
defer glfw.Terminate()
mainthread.Run(run)
}

15
vendor/github.com/faiface/pixel/pixelgl/util.go generated vendored Normal file
View File

@@ -0,0 +1,15 @@
package pixelgl
import (
"math"
"github.com/faiface/pixel"
)
func intBounds(bounds pixel.Rect) (x, y, w, h int) {
x0 := int(math.Floor(bounds.Min.X))
y0 := int(math.Floor(bounds.Min.Y))
x1 := int(math.Ceil(bounds.Max.X))
y1 := int(math.Ceil(bounds.Max.Y))
return x0, y0, x1 - x0, y1 - y0
}

426
vendor/github.com/faiface/pixel/pixelgl/window.go generated vendored Normal file
View File

@@ -0,0 +1,426 @@
package pixelgl
import (
"image"
"image/color"
"runtime"
"github.com/faiface/glhf"
"github.com/faiface/mainthread"
"github.com/faiface/pixel"
"github.com/go-gl/glfw/v3.2/glfw"
"github.com/pkg/errors"
)
// WindowConfig is a structure for specifying all possible properties of a Window. Properties are
// chosen in such a way, that you usually only need to set a few of them - defaults (zeros) should
// usually be sensible.
//
// Note that you always need to set the Bounds of a Window.
type WindowConfig struct {
// Title at the top of the Window.
Title string
// Icon specifies the icon images available to be used by the window. This is usually
// displayed in the top bar of the window or in the task bar of the desktop environment.
//
// If passed one image, it will use that image, if passed an array of images those of or
// closest to the sizes desired by the system are selected. The desired image sizes varies
// depending on platform and system settings. The selected images will be rescaled as
// needed. Good sizes include 16x16, 32x32 and 48x48.
//
// Note: Setting this value doesn't have an effect on OSX. You'll need to set the icon when
// bundling your application for release.
Icon []pixel.Picture
// Bounds specify the bounds of the Window in pixels.
Bounds pixel.Rect
// If set to nil, the Window will be windowed. Otherwise it will be fullscreen on the
// specified Monitor.
Monitor *Monitor
// Whether the Window is resizable.
Resizable bool
// Undecorated Window ommits the borders and decorations (close button, etc.).
Undecorated bool
// VSync (vertical synchronization) synchronizes Window's framerate with the framerate of
// the monitor.
VSync bool
}
// Window is a window handler. Use this type to manipulate a window (input, drawing, etc.).
type Window struct {
window *glfw.Window
bounds pixel.Rect
canvas *Canvas
vsync bool
cursorVisible bool
// need to save these to correctly restore a fullscreen window
restore struct {
xpos, ypos, width, height int
}
prevInp, currInp, tempInp struct {
mouse pixel.Vec
buttons [KeyLast + 1]bool
repeat [KeyLast + 1]bool
scroll pixel.Vec
typed string
}
}
var currWin *Window
// NewWindow creates a new Window with it's properties specified in the provided config.
//
// If Window creation fails, an error is returned (e.g. due to unavailable graphics device).
func NewWindow(cfg WindowConfig) (*Window, error) {
bool2int := map[bool]int{
true: glfw.True,
false: glfw.False,
}
w := &Window{bounds: cfg.Bounds, cursorVisible: true}
err := mainthread.CallErr(func() error {
var err error
glfw.WindowHint(glfw.ContextVersionMajor, 3)
glfw.WindowHint(glfw.ContextVersionMinor, 3)
glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)
glfw.WindowHint(glfw.Resizable, bool2int[cfg.Resizable])
glfw.WindowHint(glfw.Decorated, bool2int[!cfg.Undecorated])
var share *glfw.Window
if currWin != nil {
share = currWin.window
}
_, _, width, height := intBounds(cfg.Bounds)
w.window, err = glfw.CreateWindow(
width,
height,
cfg.Title,
nil,
share,
)
if err != nil {
return err
}
// enter the OpenGL context
w.begin()
w.end()
return nil
})
if err != nil {
return nil, errors.Wrap(err, "creating window failed")
}
if len(cfg.Icon) > 0 {
imgs := make([]image.Image, len(cfg.Icon))
for i, icon := range cfg.Icon {
pic := pixel.PictureDataFromPicture(icon)
imgs[i] = pic.Image()
}
mainthread.Call(func() {
w.window.SetIcon(imgs)
})
}
w.SetVSync(cfg.VSync)
w.initInput()
w.SetMonitor(cfg.Monitor)
w.canvas = NewCanvas(cfg.Bounds)
w.Update()
runtime.SetFinalizer(w, (*Window).Destroy)
return w, nil
}
// Destroy destroys the Window. The Window can't be used any further.
func (w *Window) Destroy() {
mainthread.Call(func() {
w.window.Destroy()
})
}
// Update swaps buffers and polls events. Call this method at the end of each frame.
func (w *Window) Update() {
mainthread.Call(func() {
_, _, oldW, oldH := intBounds(w.bounds)
newW, newH := w.window.GetSize()
w.bounds = w.bounds.ResizedMin(w.bounds.Size().Add(pixel.V(
float64(newW-oldW),
float64(newH-oldH),
)))
})
w.canvas.SetBounds(w.bounds)
mainthread.Call(func() {
w.begin()
framebufferWidth, framebufferHeight := w.window.GetFramebufferSize()
glhf.Bounds(0, 0, framebufferWidth, framebufferHeight)
glhf.Clear(0, 0, 0, 0)
w.canvas.gf.Frame().Begin()
w.canvas.gf.Frame().Blit(
nil,
0, 0, w.canvas.Texture().Width(), w.canvas.Texture().Height(),
0, 0, framebufferWidth, framebufferHeight,
)
w.canvas.gf.Frame().End()
if w.vsync {
glfw.SwapInterval(1)
} else {
glfw.SwapInterval(0)
}
w.window.SwapBuffers()
w.end()
})
w.UpdateInput()
}
// SetClosed sets the closed flag of the Window.
//
// This is useful when overriding the user's attempt to close the Window, or just to close the
// Window from within the program.
func (w *Window) SetClosed(closed bool) {
mainthread.Call(func() {
w.window.SetShouldClose(closed)
})
}
// Closed returns the closed flag of the Window, which reports whether the Window should be closed.
//
// The closed flag is automatically set when a user attempts to close the Window.
func (w *Window) Closed() bool {
var closed bool
mainthread.Call(func() {
closed = w.window.ShouldClose()
})
return closed
}
// SetTitle changes the title of the Window.
func (w *Window) SetTitle(title string) {
mainthread.Call(func() {
w.window.SetTitle(title)
})
}
// SetBounds sets the bounds of the Window in pixels. Bounds can be fractional, but the actual size
// of the window will be rounded to integers.
func (w *Window) SetBounds(bounds pixel.Rect) {
w.bounds = bounds
mainthread.Call(func() {
_, _, width, height := intBounds(bounds)
w.window.SetSize(width, height)
})
}
// SetPos sets the position, in screen coordinates, of the upper-left corner
// of the client area of the window. Position can be fractional, but the actual position
// of the window will be rounded to integers.
//
// If it is a full screen window, this function does nothing.
func (w *Window) SetPos(pos pixel.Vec) {
mainthread.Call(func() {
left, top := int(pos.X), int(pos.Y)
w.window.SetPos(left, top)
})
}
// GetPos gets the position, in screen coordinates, of the upper-left corner
// of the client area of the window. The position is rounded to integers.
func (w *Window) GetPos() pixel.Vec {
var v pixel.Vec
mainthread.Call(func() {
x, y := w.window.GetPos()
v = pixel.V(float64(x), float64(y))
})
return v
}
// Bounds returns the current bounds of the Window.
func (w *Window) Bounds() pixel.Rect {
return w.bounds
}
func (w *Window) setFullscreen(monitor *Monitor) {
mainthread.Call(func() {
w.restore.xpos, w.restore.ypos = w.window.GetPos()
w.restore.width, w.restore.height = w.window.GetSize()
mode := monitor.monitor.GetVideoMode()
w.window.SetMonitor(
monitor.monitor,
0,
0,
mode.Width,
mode.Height,
mode.RefreshRate,
)
})
}
func (w *Window) setWindowed() {
mainthread.Call(func() {
w.window.SetMonitor(
nil,
w.restore.xpos,
w.restore.ypos,
w.restore.width,
w.restore.height,
0,
)
})
}
// SetMonitor sets the Window fullscreen on the given Monitor. If the Monitor is nil, the Window
// will be restored to windowed state instead.
//
// The Window will be automatically set to the Monitor's resolution. If you want a different
// resolution, you will need to set it manually with SetBounds method.
func (w *Window) SetMonitor(monitor *Monitor) {
if w.Monitor() != monitor {
if monitor != nil {
w.setFullscreen(monitor)
} else {
w.setWindowed()
}
}
}
// Monitor returns a monitor the Window is fullscreen on. If the Window is not fullscreen, this
// function returns nil.
func (w *Window) Monitor() *Monitor {
var monitor *glfw.Monitor
mainthread.Call(func() {
monitor = w.window.GetMonitor()
})
if monitor == nil {
return nil
}
return &Monitor{
monitor: monitor,
}
}
// Focused returns true if the Window has input focus.
func (w *Window) Focused() bool {
var focused bool
mainthread.Call(func() {
focused = w.window.GetAttrib(glfw.Focused) == glfw.True
})
return focused
}
// SetVSync sets whether the Window's Update should synchronize with the monitor refresh rate.
func (w *Window) SetVSync(vsync bool) {
w.vsync = vsync
}
// VSync returns whether the Window is set to synchronize with the monitor refresh rate.
func (w *Window) VSync() bool {
return w.vsync
}
// SetCursorVisible sets the visibility of the mouse cursor inside the Window client area.
func (w *Window) SetCursorVisible(visible bool) {
w.cursorVisible = visible
mainthread.Call(func() {
if visible {
w.window.SetInputMode(glfw.CursorMode, glfw.CursorNormal)
} else {
w.window.SetInputMode(glfw.CursorMode, glfw.CursorHidden)
}
})
}
// CursorVisible returns the visibility status of the mouse cursor.
func (w *Window) CursorVisible() bool {
return w.cursorVisible
}
// Note: must be called inside the main thread.
func (w *Window) begin() {
if currWin != w {
w.window.MakeContextCurrent()
glhf.Init()
currWin = w
}
}
// Note: must be called inside the main thread.
func (w *Window) end() {
// nothing, really
}
// MakeTriangles generates a specialized copy of the supplied Triangles that will draw onto this
// Window.
//
// Window supports TrianglesPosition, TrianglesColor and TrianglesPicture.
func (w *Window) MakeTriangles(t pixel.Triangles) pixel.TargetTriangles {
return w.canvas.MakeTriangles(t)
}
// MakePicture generates a specialized copy of the supplied Picture that will draw onto this Window.
//
// Window supports PictureColor.
func (w *Window) MakePicture(p pixel.Picture) pixel.TargetPicture {
return w.canvas.MakePicture(p)
}
// SetMatrix sets a Matrix that every point will be projected by.
func (w *Window) SetMatrix(m pixel.Matrix) {
w.canvas.SetMatrix(m)
}
// SetColorMask sets a global color mask for the Window.
func (w *Window) SetColorMask(c color.Color) {
w.canvas.SetColorMask(c)
}
// SetComposeMethod sets a Porter-Duff composition method to be used in the following draws onto
// this Window.
func (w *Window) SetComposeMethod(cmp pixel.ComposeMethod) {
w.canvas.SetComposeMethod(cmp)
}
// SetSmooth sets whether the stretched Pictures drawn onto this Window should be drawn smooth or
// pixely.
func (w *Window) SetSmooth(smooth bool) {
w.canvas.SetSmooth(smooth)
}
// Smooth returns whether the stretched Pictures drawn onto this Window are set to be drawn smooth
// or pixely.
func (w *Window) Smooth() bool {
return w.canvas.Smooth()
}
// Clear clears the Window with a single color.
func (w *Window) Clear(c color.Color) {
w.canvas.Clear(c)
}
// Color returns the color of the pixel over the given position inside the Window.
func (w *Window) Color(at pixel.Vec) pixel.RGBA {
return w.canvas.Color(at)
}

117
vendor/github.com/faiface/pixel/sprite.go generated vendored Normal file
View File

@@ -0,0 +1,117 @@
package pixel
import "image/color"
// Sprite is a drawable frame of a Picture. It's anchored by the center of it's Picture's frame.
//
// Frame specifies a rectangular portion of the Picture that will be drawn. For example, this
// creates a Sprite that draws the whole Picture:
//
// sprite := pixel.NewSprite(pic, pic.Bounds())
//
// Note, that Sprite caches the results of MakePicture from Targets it's drawn to for each Picture
// it's set to. What it means is that using a Sprite with an unbounded number of Pictures leads to a
// memory leak, since Sprite caches them and never forgets. In such a situation, create a new Sprite
// for each Picture.
type Sprite struct {
tri *TrianglesData
frame Rect
d Drawer
matrix Matrix
mask RGBA
}
// NewSprite creates a Sprite from the supplied frame of a Picture.
func NewSprite(pic Picture, frame Rect) *Sprite {
tri := MakeTrianglesData(6)
s := &Sprite{
tri: tri,
d: Drawer{Triangles: tri},
}
s.matrix = IM
s.mask = Alpha(1)
s.Set(pic, frame)
return s
}
// Set sets a new frame of a Picture for this Sprite.
func (s *Sprite) Set(pic Picture, frame Rect) {
s.d.Picture = pic
if frame != s.frame {
s.frame = frame
s.calcData()
}
}
// Picture returns the current Sprite's Picture.
func (s *Sprite) Picture() Picture {
return s.d.Picture
}
// Frame returns the current Sprite's frame.
func (s *Sprite) Frame() Rect {
return s.frame
}
// Draw draws the Sprite onto the provided Target. The Sprite will be transformed by the given Matrix.
//
// This method is equivalent to calling DrawColorMask with nil color mask.
func (s *Sprite) Draw(t Target, matrix Matrix) {
s.DrawColorMask(t, matrix, nil)
}
// DrawColorMask draws the Sprite onto the provided Target. The Sprite will be transformed by the
// given Matrix and all of it's color will be multiplied by the given mask.
//
// If the mask is nil, a fully opaque white mask will be used, which causes no effect.
func (s *Sprite) DrawColorMask(t Target, matrix Matrix, mask color.Color) {
dirty := false
if matrix != s.matrix {
s.matrix = matrix
dirty = true
}
if mask == nil {
mask = Alpha(1)
}
rgba := ToRGBA(mask)
if rgba != s.mask {
s.mask = rgba
dirty = true
}
if dirty {
s.calcData()
}
s.d.Draw(t)
}
func (s *Sprite) calcData() {
var (
center = s.frame.Center()
horizontal = V(s.frame.W()/2, 0)
vertical = V(0, s.frame.H()/2)
)
(*s.tri)[0].Position = Vec{}.Sub(horizontal).Sub(vertical)
(*s.tri)[1].Position = Vec{}.Add(horizontal).Sub(vertical)
(*s.tri)[2].Position = Vec{}.Add(horizontal).Add(vertical)
(*s.tri)[3].Position = Vec{}.Sub(horizontal).Sub(vertical)
(*s.tri)[4].Position = Vec{}.Add(horizontal).Add(vertical)
(*s.tri)[5].Position = Vec{}.Sub(horizontal).Add(vertical)
for i := range *s.tri {
(*s.tri)[i].Color = s.mask
(*s.tri)[i].Picture = center.Add((*s.tri)[i].Position)
(*s.tri)[i].Intensity = 1
}
// matrix and mask
for i := range *s.tri {
(*s.tri)[i].Position = s.matrix.Project((*s.tri)[i].Position)
(*s.tri)[i].Color = s.mask
}
s.d.Dirty()
}