implement Picture for Canvas

This commit is contained in:
faiface 2017-03-07 17:45:46 +01:00
parent ee634c2a28
commit f65ea40e19
3 changed files with 85 additions and 24 deletions

View File

@ -12,8 +12,6 @@ import (
"github.com/pkg/errors"
)
//TODO: make Canvas a Picture
// Canvas is an off-screen rectangular BasicTarget that you can draw onto.
//
// It supports TrianglesPosition, TrianglesColor, TrianglesPicture and PictureColor.
@ -26,8 +24,11 @@ type Canvas struct {
mat mgl32.Mat3
col mgl32.Vec4
borders pixel.Rect
bounds pixel.Rect
pixels []uint8
dirty bool
bounds pixel.Rect
orig *Canvas
}
// 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(),
col: mgl32.Vec4{1, 1, 1, 1},
}
c.orig = c
mainthread.Call(func() {
var err error
@ -79,7 +81,7 @@ func (c *Canvas) MakePicture(p pixel.Picture) pixel.TargetPicture {
}
bounds := p.Bounds()
bx, by, bw, bh := discreteBounds(bounds)
bx, by, bw, bh := intBounds(bounds)
pixels := make([]uint8, 4*bw*bh)
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.
//
// 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) {
if c.Bounds() == bounds {
return
@ -146,14 +151,14 @@ func (c *Canvas) SetBounds(bounds pixel.Rect) {
mainthread.Call(func() {
oldF := c.f
_, _, w, h := discreteBounds(bounds)
_, _, w, h := intBounds(bounds)
c.f = glhf.NewFrame(w, h, c.smooth)
// preserve old content
if oldF != nil {
relBounds := c.bounds
relBounds.Pos -= bounds.Pos
ox, oy, ow, oh := discreteBounds(relBounds)
ox, oy, ow, oh := intBounds(relBounds)
oldF.Blit(
c.f,
ox, oy, ox+ow, oy+oh,
@ -161,8 +166,10 @@ func (c *Canvas) SetBounds(bounds pixel.Rect) {
)
}
})
c.borders = bounds
c.bounds = bounds
c.orig = c // detach from the Original
c.dirty = true
}
// Bounds returns the rectangular bounds of the Canvas.
@ -182,10 +189,22 @@ func (c *Canvas) Smooth() bool {
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.
func (c *Canvas) Clear(color color.Color) {
c.orig.dirty = true
nrgba := pixel.NRGBAModel.Convert(color).(pixel.NRGBA)
mainthread.CallNonBlock(func() {
c.setGlhfBounds()
c.f.Begin()
glhf.Clear(
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 {
*GLTriangles
@ -204,24 +266,22 @@ type canvasTriangles struct {
}
func (ct *canvasTriangles) draw(cp *canvasPicture) {
ct.c.orig.dirty = true
// save the current state vars to avoid race condition
mat := ct.c.mat
col := ct.c.col
mainthread.CallNonBlock(func() {
bounds := ct.c.bounds
bounds.Pos -= ct.c.borders.Pos
bx, by, bw, bh := discreteBounds(bounds)
glhf.Bounds(bx, by, bw, bh)
ct.c.setGlhfBounds()
ct.c.f.Begin()
ct.c.s.Begin()
ct.c.s.SetUniformAttr(canvasBounds, mgl32.Vec4{
float32(cp.c.bounds.X()),
float32(cp.c.bounds.Y()),
float32(cp.c.bounds.W()),
float32(cp.c.bounds.H()),
float32(ct.c.bounds.X()),
float32(ct.c.bounds.Y()),
float32(ct.c.bounds.W()),
float32(ct.c.bounds.H()),
})
ct.c.s.SetUniformAttr(canvasTransform, mat)
ct.c.s.SetUniformAttr(canvasColorMask, col)
@ -341,11 +401,12 @@ out vec2 Texture;
out float Intensity;
uniform mat3 transform;
uniform vec4 borders;
uniform vec4 bounds;
void main() {
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);
Color = color;
Texture = texture;

View File

@ -6,7 +6,7 @@ import (
"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()))
y0 := int(math.Floor(bounds.Pos.Y()))
x1 := int(math.Ceil(bounds.Pos.X() + bounds.Size.X()))

View File

@ -105,7 +105,7 @@ func NewWindow(cfg WindowConfig) (*Window, error) {
if currentWindow != nil {
share = currentWindow.window
}
_, _, width, height := discreteBounds(cfg.Bounds)
_, _, width, height := intBounds(cfg.Bounds)
w.window, err = glfw.CreateWindow(
width,
height,
@ -166,14 +166,14 @@ func (w *Window) Update() {
mainthread.Call(func() {
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)
w.canvas.f.Begin()
w.canvas.f.Blit(
nil,
0, 0, w.canvas.f.Width(), w.canvas.f.Height(),
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.Texture.Width(), w.canvas.f.Texture.Height(),
)
w.canvas.f.End()
@ -220,7 +220,7 @@ func (w *Window) SetTitle(title string) {
func (w *Window) SetBounds(bounds pixel.Rect) {
w.bounds = bounds
mainthread.Call(func() {
_, _, width, height := discreteBounds(bounds)
_, _, width, height := intBounds(bounds)
w.window.SetSize(width, height)
})
}