implement Picture for Canvas
This commit is contained in:
parent
ee634c2a28
commit
f65ea40e19
|
@ -12,8 +12,6 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
//TODO: make Canvas a Picture
|
|
||||||
|
|
||||||
// Canvas is an off-screen rectangular BasicTarget that you can draw onto.
|
// Canvas is an off-screen rectangular BasicTarget that you can draw onto.
|
||||||
//
|
//
|
||||||
// It supports TrianglesPosition, TrianglesColor, TrianglesPicture and PictureColor.
|
// It supports TrianglesPosition, TrianglesColor, TrianglesPicture and PictureColor.
|
||||||
|
@ -26,8 +24,11 @@ type Canvas struct {
|
||||||
mat mgl32.Mat3
|
mat mgl32.Mat3
|
||||||
col mgl32.Vec4
|
col mgl32.Vec4
|
||||||
|
|
||||||
borders pixel.Rect
|
pixels []uint8
|
||||||
|
dirty bool
|
||||||
|
|
||||||
bounds pixel.Rect
|
bounds pixel.Rect
|
||||||
|
orig *Canvas
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCanvas creates a new empty, fully transparent Canvas with given bounds. If the smooth flag
|
// NewCanvas creates a new empty, fully transparent Canvas with given bounds. If the smooth flag
|
||||||
|
@ -38,6 +39,7 @@ func NewCanvas(bounds pixel.Rect, smooth bool) *Canvas {
|
||||||
mat: mgl32.Ident3(),
|
mat: mgl32.Ident3(),
|
||||||
col: mgl32.Vec4{1, 1, 1, 1},
|
col: mgl32.Vec4{1, 1, 1, 1},
|
||||||
}
|
}
|
||||||
|
c.orig = c
|
||||||
|
|
||||||
mainthread.Call(func() {
|
mainthread.Call(func() {
|
||||||
var err error
|
var err error
|
||||||
|
@ -79,7 +81,7 @@ func (c *Canvas) MakePicture(p pixel.Picture) pixel.TargetPicture {
|
||||||
}
|
}
|
||||||
|
|
||||||
bounds := p.Bounds()
|
bounds := p.Bounds()
|
||||||
bx, by, bw, bh := discreteBounds(bounds)
|
bx, by, bw, bh := intBounds(bounds)
|
||||||
|
|
||||||
pixels := make([]uint8, 4*bw*bh)
|
pixels := make([]uint8, 4*bw*bh)
|
||||||
if p, ok := p.(pixel.PictureColor); ok {
|
if p, ok := p.(pixel.PictureColor); ok {
|
||||||
|
@ -138,6 +140,9 @@ func (c *Canvas) SetColorMask(col color.Color) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetBounds resizes the Canvas to the new bounds. Old content will be preserved.
|
// SetBounds resizes the Canvas to the new bounds. Old content will be preserved.
|
||||||
|
//
|
||||||
|
// If this Canvas was created using Slice-ing, then the relation between this Canvas and it's
|
||||||
|
// Original is unspecified (but Original will always return valid stuff).
|
||||||
func (c *Canvas) SetBounds(bounds pixel.Rect) {
|
func (c *Canvas) SetBounds(bounds pixel.Rect) {
|
||||||
if c.Bounds() == bounds {
|
if c.Bounds() == bounds {
|
||||||
return
|
return
|
||||||
|
@ -146,14 +151,14 @@ func (c *Canvas) SetBounds(bounds pixel.Rect) {
|
||||||
mainthread.Call(func() {
|
mainthread.Call(func() {
|
||||||
oldF := c.f
|
oldF := c.f
|
||||||
|
|
||||||
_, _, w, h := discreteBounds(bounds)
|
_, _, w, h := intBounds(bounds)
|
||||||
c.f = glhf.NewFrame(w, h, c.smooth)
|
c.f = glhf.NewFrame(w, h, c.smooth)
|
||||||
|
|
||||||
// preserve old content
|
// preserve old content
|
||||||
if oldF != nil {
|
if oldF != nil {
|
||||||
relBounds := c.bounds
|
relBounds := c.bounds
|
||||||
relBounds.Pos -= bounds.Pos
|
relBounds.Pos -= bounds.Pos
|
||||||
ox, oy, ow, oh := discreteBounds(relBounds)
|
ox, oy, ow, oh := intBounds(relBounds)
|
||||||
oldF.Blit(
|
oldF.Blit(
|
||||||
c.f,
|
c.f,
|
||||||
ox, oy, ox+ow, oy+oh,
|
ox, oy, ox+ow, oy+oh,
|
||||||
|
@ -161,8 +166,10 @@ func (c *Canvas) SetBounds(bounds pixel.Rect) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
c.borders = bounds
|
|
||||||
c.bounds = bounds
|
c.bounds = bounds
|
||||||
|
c.orig = c // detach from the Original
|
||||||
|
c.dirty = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bounds returns the rectangular bounds of the Canvas.
|
// Bounds returns the rectangular bounds of the Canvas.
|
||||||
|
@ -182,10 +189,22 @@ func (c *Canvas) Smooth() bool {
|
||||||
return c.smooth
|
return c.smooth
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// must be manually called inside mainthread
|
||||||
|
func (c *Canvas) setGlhfBounds() {
|
||||||
|
bounds := c.bounds
|
||||||
|
bounds.Pos -= c.orig.bounds.Pos
|
||||||
|
bx, by, bw, bh := intBounds(bounds)
|
||||||
|
glhf.Bounds(bx, by, bw, bh)
|
||||||
|
}
|
||||||
|
|
||||||
// Clear fill the whole Canvas with a single color.
|
// Clear fill the whole Canvas with a single color.
|
||||||
func (c *Canvas) Clear(color color.Color) {
|
func (c *Canvas) Clear(color color.Color) {
|
||||||
|
c.orig.dirty = true
|
||||||
|
|
||||||
nrgba := pixel.NRGBAModel.Convert(color).(pixel.NRGBA)
|
nrgba := pixel.NRGBAModel.Convert(color).(pixel.NRGBA)
|
||||||
|
|
||||||
mainthread.CallNonBlock(func() {
|
mainthread.CallNonBlock(func() {
|
||||||
|
c.setGlhfBounds()
|
||||||
c.f.Begin()
|
c.f.Begin()
|
||||||
glhf.Clear(
|
glhf.Clear(
|
||||||
float32(nrgba.R),
|
float32(nrgba.R),
|
||||||
|
@ -197,6 +216,49 @@ func (c *Canvas) Clear(color color.Color) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Slice returns a sub-Canvas with the specified Bounds.
|
||||||
|
//
|
||||||
|
// The returned value is *Canvas, the type of the return value is a general pixel.Picture just so
|
||||||
|
// that Canvas implements pixel.Picture interface.
|
||||||
|
func (c *Canvas) Slice(bounds pixel.Rect) pixel.Picture {
|
||||||
|
sc := new(Canvas)
|
||||||
|
*sc = *c
|
||||||
|
sc.bounds = bounds
|
||||||
|
return sc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Original returns the most original Canvas that this Canvas was created from using Slice-ing.
|
||||||
|
//
|
||||||
|
// The returned value is *Canvas, the type of the return value is a general pixel.Picture just so
|
||||||
|
// that Canvas implements pixel.Picture interface.
|
||||||
|
func (c *Canvas) Original() pixel.Picture {
|
||||||
|
return c.orig
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color returns the color of the pixel over the given position inside the Canvas.
|
||||||
|
func (c *Canvas) Color(at pixel.Vec) pixel.NRGBA {
|
||||||
|
if c.orig.dirty {
|
||||||
|
mainthread.Call(func() {
|
||||||
|
c.f.Texture.Begin()
|
||||||
|
c.orig.pixels = c.f.Texture.Pixels(0, 0, c.f.Texture.Width(), c.f.Texture.Height())
|
||||||
|
c.f.Texture.End()
|
||||||
|
})
|
||||||
|
c.orig.dirty = false
|
||||||
|
}
|
||||||
|
if !c.bounds.Contains(at) {
|
||||||
|
return pixel.NRGBA{}
|
||||||
|
}
|
||||||
|
bx, by, bw, _ := intBounds(c.orig.bounds)
|
||||||
|
x, y := int(at.X())-bx, int(at.Y())-by
|
||||||
|
off := y*bw + x
|
||||||
|
return pixel.NRGBA{
|
||||||
|
R: float64(c.orig.pixels[off*4+0]) / 255,
|
||||||
|
G: float64(c.orig.pixels[off*4+1]) / 255,
|
||||||
|
B: float64(c.orig.pixels[off*4+2]) / 255,
|
||||||
|
A: float64(c.orig.pixels[off*4+3]) / 255,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type canvasTriangles struct {
|
type canvasTriangles struct {
|
||||||
*GLTriangles
|
*GLTriangles
|
||||||
|
|
||||||
|
@ -204,24 +266,22 @@ type canvasTriangles struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ct *canvasTriangles) draw(cp *canvasPicture) {
|
func (ct *canvasTriangles) draw(cp *canvasPicture) {
|
||||||
|
ct.c.orig.dirty = true
|
||||||
|
|
||||||
// save the current state vars to avoid race condition
|
// save the current state vars to avoid race condition
|
||||||
mat := ct.c.mat
|
mat := ct.c.mat
|
||||||
col := ct.c.col
|
col := ct.c.col
|
||||||
|
|
||||||
mainthread.CallNonBlock(func() {
|
mainthread.CallNonBlock(func() {
|
||||||
bounds := ct.c.bounds
|
ct.c.setGlhfBounds()
|
||||||
bounds.Pos -= ct.c.borders.Pos
|
|
||||||
bx, by, bw, bh := discreteBounds(bounds)
|
|
||||||
glhf.Bounds(bx, by, bw, bh)
|
|
||||||
|
|
||||||
ct.c.f.Begin()
|
ct.c.f.Begin()
|
||||||
ct.c.s.Begin()
|
ct.c.s.Begin()
|
||||||
|
|
||||||
ct.c.s.SetUniformAttr(canvasBounds, mgl32.Vec4{
|
ct.c.s.SetUniformAttr(canvasBounds, mgl32.Vec4{
|
||||||
float32(cp.c.bounds.X()),
|
float32(ct.c.bounds.X()),
|
||||||
float32(cp.c.bounds.Y()),
|
float32(ct.c.bounds.Y()),
|
||||||
float32(cp.c.bounds.W()),
|
float32(ct.c.bounds.W()),
|
||||||
float32(cp.c.bounds.H()),
|
float32(ct.c.bounds.H()),
|
||||||
})
|
})
|
||||||
ct.c.s.SetUniformAttr(canvasTransform, mat)
|
ct.c.s.SetUniformAttr(canvasTransform, mat)
|
||||||
ct.c.s.SetUniformAttr(canvasColorMask, col)
|
ct.c.s.SetUniformAttr(canvasColorMask, col)
|
||||||
|
@ -341,11 +401,12 @@ out vec2 Texture;
|
||||||
out float Intensity;
|
out float Intensity;
|
||||||
|
|
||||||
uniform mat3 transform;
|
uniform mat3 transform;
|
||||||
|
uniform vec4 borders;
|
||||||
uniform vec4 bounds;
|
uniform vec4 bounds;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec2 transPos = (transform * vec3(position, 1.0)).xy;
|
vec2 transPos = (transform * vec3(position, 1.0)).xy;
|
||||||
vec2 normPos = 2 * (transPos - bounds.xy) / (bounds.zw) - vec2(1, 1);
|
vec2 normPos = (transPos - bounds.xy) / (bounds.zw) * 2 - vec2(1, 1);
|
||||||
gl_Position = vec4(normPos, 0.0, 1.0);
|
gl_Position = vec4(normPos, 0.0, 1.0);
|
||||||
Color = color;
|
Color = color;
|
||||||
Texture = texture;
|
Texture = texture;
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"github.com/faiface/pixel"
|
"github.com/faiface/pixel"
|
||||||
)
|
)
|
||||||
|
|
||||||
func discreteBounds(bounds pixel.Rect) (x, y, w, h int) {
|
func intBounds(bounds pixel.Rect) (x, y, w, h int) {
|
||||||
x0 := int(math.Floor(bounds.Pos.X()))
|
x0 := int(math.Floor(bounds.Pos.X()))
|
||||||
y0 := int(math.Floor(bounds.Pos.Y()))
|
y0 := int(math.Floor(bounds.Pos.Y()))
|
||||||
x1 := int(math.Ceil(bounds.Pos.X() + bounds.Size.X()))
|
x1 := int(math.Ceil(bounds.Pos.X() + bounds.Size.X()))
|
||||||
|
|
|
@ -105,7 +105,7 @@ func NewWindow(cfg WindowConfig) (*Window, error) {
|
||||||
if currentWindow != nil {
|
if currentWindow != nil {
|
||||||
share = currentWindow.window
|
share = currentWindow.window
|
||||||
}
|
}
|
||||||
_, _, width, height := discreteBounds(cfg.Bounds)
|
_, _, width, height := intBounds(cfg.Bounds)
|
||||||
w.window, err = glfw.CreateWindow(
|
w.window, err = glfw.CreateWindow(
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
@ -166,14 +166,14 @@ func (w *Window) Update() {
|
||||||
mainthread.Call(func() {
|
mainthread.Call(func() {
|
||||||
w.begin()
|
w.begin()
|
||||||
|
|
||||||
glhf.Bounds(0, 0, w.canvas.f.Width(), w.canvas.f.Height())
|
glhf.Bounds(0, 0, w.canvas.f.Texture.Width(), w.canvas.f.Texture.Height())
|
||||||
|
|
||||||
glhf.Clear(0, 0, 0, 0)
|
glhf.Clear(0, 0, 0, 0)
|
||||||
w.canvas.f.Begin()
|
w.canvas.f.Begin()
|
||||||
w.canvas.f.Blit(
|
w.canvas.f.Blit(
|
||||||
nil,
|
nil,
|
||||||
0, 0, w.canvas.f.Width(), w.canvas.f.Height(),
|
0, 0, w.canvas.f.Texture.Width(), w.canvas.f.Texture.Height(),
|
||||||
0, 0, w.canvas.f.Width(), w.canvas.f.Height(),
|
0, 0, w.canvas.f.Texture.Width(), w.canvas.f.Texture.Height(),
|
||||||
)
|
)
|
||||||
w.canvas.f.End()
|
w.canvas.f.End()
|
||||||
|
|
||||||
|
@ -220,7 +220,7 @@ func (w *Window) SetTitle(title string) {
|
||||||
func (w *Window) SetBounds(bounds pixel.Rect) {
|
func (w *Window) SetBounds(bounds pixel.Rect) {
|
||||||
w.bounds = bounds
|
w.bounds = bounds
|
||||||
mainthread.Call(func() {
|
mainthread.Call(func() {
|
||||||
_, _, width, height := discreteBounds(bounds)
|
_, _, width, height := intBounds(bounds)
|
||||||
w.window.SetSize(width, height)
|
w.window.SetSize(width, height)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue