moved offscreen canvas to backend
This commit is contained in:
parent
a0dfb6f530
commit
8dc91b34f5
12 changed files with 122 additions and 211 deletions
|
@ -10,6 +10,8 @@ import (
|
||||||
// drawing. This enables the backend to be implemented by
|
// drawing. This enables the backend to be implemented by
|
||||||
// various methods (OpenGL, but also other APIs or software)
|
// various methods (OpenGL, but also other APIs or software)
|
||||||
type Backend interface {
|
type Backend interface {
|
||||||
|
Size() (int, int)
|
||||||
|
|
||||||
LoadImage(img image.Image) (Image, error)
|
LoadImage(img image.Image) (Image, error)
|
||||||
LoadLinearGradient(data *LinearGradientData) LinearGradient
|
LoadLinearGradient(data *LinearGradientData) LinearGradient
|
||||||
LoadRadialGradient(data *RadialGradientData) RadialGradient
|
LoadRadialGradient(data *RadialGradientData) RadialGradient
|
||||||
|
|
|
@ -7,11 +7,15 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b *GoGLBackend) ClearClip() {
|
func (b *GoGLBackend) ClearClip() {
|
||||||
|
b.activate()
|
||||||
|
|
||||||
gl.StencilMask(0xFF)
|
gl.StencilMask(0xFF)
|
||||||
gl.Clear(gl.STENCIL_BUFFER_BIT)
|
gl.Clear(gl.STENCIL_BUFFER_BIT)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *GoGLBackend) Clip(pts [][2]float64) {
|
func (b *GoGLBackend) Clip(pts [][2]float64) {
|
||||||
|
b.activate()
|
||||||
|
|
||||||
b.ptsBuf = b.ptsBuf[:0]
|
b.ptsBuf = b.ptsBuf[:0]
|
||||||
b.ptsBuf = append(b.ptsBuf,
|
b.ptsBuf = append(b.ptsBuf,
|
||||||
0, 0,
|
0, 0,
|
||||||
|
|
|
@ -10,6 +10,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b *GoGLBackend) Clear(pts [4][2]float64) {
|
func (b *GoGLBackend) Clear(pts [4][2]float64) {
|
||||||
|
b.activate()
|
||||||
|
|
||||||
// first check if the four points are aligned to form a nice rectangle, which can be more easily
|
// first check if the four points are aligned to form a nice rectangle, which can be more easily
|
||||||
// cleared using glScissor and glClear
|
// cleared using glScissor and glClear
|
||||||
aligned := pts[0][0] == pts[1][0] && pts[2][0] == pts[3][0] && pts[0][1] == pts[3][1] && pts[1][1] == pts[2][1]
|
aligned := pts[0][0] == pts[1][0] && pts[2][0] == pts[3][0] && pts[0][1] == pts[3][1] && pts[1][1] == pts[2][1]
|
||||||
|
@ -68,6 +70,8 @@ func (b *GoGLBackend) clearRect(x, y, w, h int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *GoGLBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) {
|
func (b *GoGLBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) {
|
||||||
|
b.activate()
|
||||||
|
|
||||||
if style.Blur > 0 {
|
if style.Blur > 0 {
|
||||||
b.offscr1.alpha = true
|
b.offscr1.alpha = true
|
||||||
b.enableTextureRenderTarget(&b.offscr1)
|
b.enableTextureRenderTarget(&b.offscr1)
|
||||||
|
@ -140,6 +144,8 @@ func (b *GoGLBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *GoGLBackend) FillImageMask(style *backendbase.FillStyle, mask *image.Alpha, pts [][2]float64) {
|
func (b *GoGLBackend) FillImageMask(style *backendbase.FillStyle, mask *image.Alpha, pts [][2]float64) {
|
||||||
|
b.activate()
|
||||||
|
|
||||||
w, h := mask.Rect.Dx(), mask.Rect.Dy()
|
w, h := mask.Rect.Dx(), mask.Rect.Dy()
|
||||||
|
|
||||||
gl.ActiveTexture(gl.TEXTURE1)
|
gl.ActiveTexture(gl.TEXTURE1)
|
||||||
|
|
|
@ -42,6 +42,10 @@ type GoGLBackend struct {
|
||||||
|
|
||||||
ptsBuf []float32
|
ptsBuf []float32
|
||||||
|
|
||||||
|
offscreen bool
|
||||||
|
offscrBuf offscreenBuffer
|
||||||
|
offscrImg Image
|
||||||
|
|
||||||
glChan chan func()
|
glChan chan func()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +58,7 @@ type offscreenBuffer struct {
|
||||||
alpha bool
|
alpha bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(x, y, w, h int) (backendbase.Backend, error) {
|
func New(x, y, w, h int) (*GoGLBackend, error) {
|
||||||
err := gl.Init()
|
err := gl.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -210,15 +214,31 @@ func New(x, y, w, h int) (backendbase.Backend, error) {
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewOffscreen(w, h int, alpha bool) (*GoGLBackend, error) {
|
||||||
|
b, err := New(0, 0, w, h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b.offscreen = true
|
||||||
|
b.offscrBuf.alpha = alpha
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
// SetBounds updates the bounds of the canvas. This would
|
// SetBounds updates the bounds of the canvas. This would
|
||||||
// usually be called for example when the window is resized
|
// usually be called for example when the window is resized
|
||||||
func (b *GoGLBackend) SetBounds(x, y, w, h int) {
|
func (b *GoGLBackend) SetBounds(x, y, w, h int) {
|
||||||
|
if !b.offscreen {
|
||||||
b.x, b.y = x, y
|
b.x, b.y = x, y
|
||||||
b.fx, b.fy = float64(x), float64(y)
|
b.fx, b.fy = float64(x), float64(y)
|
||||||
|
}
|
||||||
b.w, b.h = w, h
|
b.w, b.h = w, h
|
||||||
b.fw, b.fh = float64(w), float64(h)
|
b.fw, b.fh = float64(w), float64(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *GoGLBackend) Size() (int, int) {
|
||||||
|
return b.w, b.h
|
||||||
|
}
|
||||||
|
|
||||||
func glError() error {
|
func glError() error {
|
||||||
glErr := gl.GetError()
|
glErr := gl.GetError()
|
||||||
if glErr != gl.NO_ERROR {
|
if glErr != gl.NO_ERROR {
|
||||||
|
@ -227,21 +247,44 @@ func glError() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Activate makes this GL backend active and sets the viewport. Only
|
var activeContext *GoGLBackend
|
||||||
// needs to be called if any other GL code changes the viewport
|
|
||||||
func (b *GoGLBackend) Activate() {
|
func (b *GoGLBackend) activate() {
|
||||||
// if b.offscreen {
|
if activeContext != b {
|
||||||
// gl.Viewport(0, 0, int32(cv.w), int32(cv.h))
|
activeContext = b
|
||||||
// cv.enableTextureRenderTarget(&cv.offscrBuf)
|
if b.offscreen {
|
||||||
// cv.offscrImg.w = cv.offscrBuf.w
|
gl.Viewport(0, 0, int32(b.w), int32(b.h))
|
||||||
// cv.offscrImg.h = cv.offscrBuf.h
|
b.enableTextureRenderTarget(&b.offscrBuf)
|
||||||
// cv.offscrImg.tex = cv.offscrBuf.tex
|
b.offscrImg.w = b.offscrBuf.w
|
||||||
// } else {
|
b.offscrImg.h = b.offscrBuf.h
|
||||||
|
b.offscrImg.tex = b.offscrBuf.tex
|
||||||
|
} else {
|
||||||
gl.Viewport(int32(b.x), int32(b.y), int32(b.w), int32(b.h))
|
gl.Viewport(int32(b.x), int32(b.y), int32(b.w), int32(b.h))
|
||||||
b.disableTextureRenderTarget()
|
b.disableTextureRenderTarget()
|
||||||
// }
|
}
|
||||||
// b.applyScissor()
|
}
|
||||||
gl.Clear(gl.STENCIL_BUFFER_BIT)
|
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case f := <-b.glChan:
|
||||||
|
f()
|
||||||
|
default:
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *GoGLBackend) DeleteOffscreen() {
|
||||||
|
if !b.offscreen {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gl.DeleteTextures(1, &b.offscrBuf.tex)
|
||||||
|
gl.DeleteFramebuffers(1, &b.offscrBuf.frameBuf)
|
||||||
|
gl.DeleteRenderbuffers(1, &b.offscrBuf.renderStencilBuf)
|
||||||
|
b.offscreen = false
|
||||||
|
|
||||||
|
b.activate()
|
||||||
}
|
}
|
||||||
|
|
||||||
type glColor struct {
|
type glColor struct {
|
||||||
|
@ -439,11 +482,11 @@ func (b *GoGLBackend) enableTextureRenderTarget(offscr *offscreenBuffer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *GoGLBackend) disableTextureRenderTarget() {
|
func (b *GoGLBackend) disableTextureRenderTarget() {
|
||||||
// if b.offscreen {
|
if b.offscreen {
|
||||||
// b.enableTextureRenderTarget(&b.offscrBuf)
|
b.enableTextureRenderTarget(&b.offscrBuf)
|
||||||
// } else {
|
} else {
|
||||||
gl.BindFramebuffer(gl.FRAMEBUFFER, 0)
|
gl.BindFramebuffer(gl.FRAMEBUFFER, 0)
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type mat [9]float64
|
type mat [9]float64
|
||||||
|
|
|
@ -25,6 +25,7 @@ type RadialGradient struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type gradient struct {
|
type gradient struct {
|
||||||
|
b *GoGLBackend
|
||||||
from, to vec
|
from, to vec
|
||||||
tex uint32
|
tex uint32
|
||||||
loaded bool
|
loaded bool
|
||||||
|
@ -33,8 +34,10 @@ type gradient struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *GoGLBackend) LoadLinearGradient(data *backendbase.LinearGradientData) backendbase.LinearGradient {
|
func (b *GoGLBackend) LoadLinearGradient(data *backendbase.LinearGradientData) backendbase.LinearGradient {
|
||||||
|
b.activate()
|
||||||
|
|
||||||
lg := &LinearGradient{
|
lg := &LinearGradient{
|
||||||
gradient: gradient{from: vec{data.X0, data.Y0}, to: vec{data.X1, data.Y1}, opaque: true},
|
gradient: gradient{b: b, from: vec{data.X0, data.Y0}, to: vec{data.X1, data.Y1}, opaque: true},
|
||||||
}
|
}
|
||||||
gl.GenTextures(1, &lg.tex)
|
gl.GenTextures(1, &lg.tex)
|
||||||
gl.ActiveTexture(gl.TEXTURE0)
|
gl.ActiveTexture(gl.TEXTURE0)
|
||||||
|
@ -53,8 +56,10 @@ func (b *GoGLBackend) LoadLinearGradient(data *backendbase.LinearGradientData) b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *GoGLBackend) LoadRadialGradient(data *backendbase.RadialGradientData) backendbase.RadialGradient {
|
func (b *GoGLBackend) LoadRadialGradient(data *backendbase.RadialGradientData) backendbase.RadialGradient {
|
||||||
|
b.activate()
|
||||||
|
|
||||||
rg := &RadialGradient{
|
rg := &RadialGradient{
|
||||||
gradient: gradient{from: vec{data.X0, data.Y0}, to: vec{data.X1, data.Y1}, opaque: true},
|
gradient: gradient{b: b, from: vec{data.X0, data.Y0}, to: vec{data.X1, data.Y1}, opaque: true},
|
||||||
radFrom: data.RadFrom,
|
radFrom: data.RadFrom,
|
||||||
radTo: data.RadTo,
|
radTo: data.RadTo,
|
||||||
}
|
}
|
||||||
|
@ -76,6 +81,8 @@ func (b *GoGLBackend) LoadRadialGradient(data *backendbase.RadialGradientData) b
|
||||||
|
|
||||||
// Delete explicitly deletes the gradient
|
// Delete explicitly deletes the gradient
|
||||||
func (g *gradient) Delete() {
|
func (g *gradient) Delete() {
|
||||||
|
g.b.activate()
|
||||||
|
|
||||||
gl.DeleteTextures(1, &g.tex)
|
gl.DeleteTextures(1, &g.tex)
|
||||||
g.deleted = true
|
g.deleted = true
|
||||||
}
|
}
|
||||||
|
@ -91,6 +98,8 @@ func (g *gradient) load(stops backendbase.Gradient) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g.b.activate()
|
||||||
|
|
||||||
gl.ActiveTexture(gl.TEXTURE0)
|
gl.ActiveTexture(gl.TEXTURE0)
|
||||||
gl.BindTexture(gl.TEXTURE_2D, g.tex)
|
gl.BindTexture(gl.TEXTURE_2D, g.tex)
|
||||||
var pixels [2048 * 4]byte
|
var pixels [2048 * 4]byte
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
|
|
||||||
// GetImageData returns an RGBA image of the current image
|
// GetImageData returns an RGBA image of the current image
|
||||||
func (b *GoGLBackend) GetImageData(x, y, w, h int) *image.RGBA {
|
func (b *GoGLBackend) GetImageData(x, y, w, h int) *image.RGBA {
|
||||||
// cv.activate()
|
b.activate()
|
||||||
|
|
||||||
if x < 0 {
|
if x < 0 {
|
||||||
w += x
|
w += x
|
||||||
|
@ -44,7 +44,7 @@ func (b *GoGLBackend) GetImageData(x, y, w, h int) *image.RGBA {
|
||||||
|
|
||||||
// PutImageData puts the given image at the given x/y coordinates
|
// PutImageData puts the given image at the given x/y coordinates
|
||||||
func (b *GoGLBackend) PutImageData(img *image.RGBA, x, y int) {
|
func (b *GoGLBackend) PutImageData(img *image.RGBA, x, y int) {
|
||||||
// cv.activate()
|
b.activate()
|
||||||
|
|
||||||
gl.ActiveTexture(gl.TEXTURE0)
|
gl.ActiveTexture(gl.TEXTURE0)
|
||||||
if b.imageBufTex == 0 {
|
if b.imageBufTex == 0 {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
|
|
||||||
// Image represents a loaded image that can be used in various drawing functions
|
// Image represents a loaded image that can be used in various drawing functions
|
||||||
type Image struct {
|
type Image struct {
|
||||||
|
b *GoGLBackend
|
||||||
w, h int
|
w, h int
|
||||||
tex uint32
|
tex uint32
|
||||||
deleted bool
|
deleted bool
|
||||||
|
@ -19,6 +20,8 @@ type Image struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *GoGLBackend) LoadImage(src image.Image) (backendbase.Image, error) {
|
func (b *GoGLBackend) LoadImage(src image.Image) (backendbase.Image, error) {
|
||||||
|
b.activate()
|
||||||
|
|
||||||
var tex uint32
|
var tex uint32
|
||||||
gl.GenTextures(1, &tex)
|
gl.GenTextures(1, &tex)
|
||||||
gl.ActiveTexture(gl.TEXTURE0)
|
gl.ActiveTexture(gl.TEXTURE0)
|
||||||
|
@ -184,6 +187,8 @@ func (img *Image) Size() (int, int) { return img.w, img.h }
|
||||||
// Delete deletes the image from memory. Any draw calls
|
// Delete deletes the image from memory. Any draw calls
|
||||||
// with a deleted image will not do anything
|
// with a deleted image will not do anything
|
||||||
func (img *Image) Delete() {
|
func (img *Image) Delete() {
|
||||||
|
img.b.activate()
|
||||||
|
|
||||||
gl.DeleteTextures(1, &img.tex)
|
gl.DeleteTextures(1, &img.tex)
|
||||||
img.deleted = true
|
img.deleted = true
|
||||||
}
|
}
|
||||||
|
@ -194,6 +199,8 @@ func (img *Image) IsDeleted() bool { return img.deleted }
|
||||||
|
|
||||||
// Replace replaces the image with the new one
|
// Replace replaces the image with the new one
|
||||||
func (img *Image) Replace(src image.Image) error {
|
func (img *Image) Replace(src image.Image) error {
|
||||||
|
img.b.activate()
|
||||||
|
|
||||||
gl.ActiveTexture(gl.TEXTURE0)
|
gl.ActiveTexture(gl.TEXTURE0)
|
||||||
gl.BindTexture(gl.TEXTURE_2D, img.tex)
|
gl.BindTexture(gl.TEXTURE_2D, img.tex)
|
||||||
newImg, err := loadImage(src, img.tex)
|
newImg, err := loadImage(src, img.tex)
|
||||||
|
@ -209,6 +216,8 @@ func (img *Image) Replace(src image.Image) error {
|
||||||
func (img *Image) IsOpaque() bool { return img.opaque }
|
func (img *Image) IsOpaque() bool { return img.opaque }
|
||||||
|
|
||||||
func (b *GoGLBackend) DrawImage(dimg backendbase.Image, sx, sy, sw, sh, dx, dy, dw, dh float64, alpha float64) {
|
func (b *GoGLBackend) DrawImage(dimg backendbase.Image, sx, sy, sw, sh, dx, dy, dw, dh float64, alpha float64) {
|
||||||
|
b.activate()
|
||||||
|
|
||||||
img := dimg.(*Image)
|
img := dimg.(*Image)
|
||||||
|
|
||||||
sx /= float64(img.w)
|
sx /= float64(img.w)
|
||||||
|
|
153
canvas.go
153
canvas.go
|
@ -20,8 +20,8 @@ import (
|
||||||
type Canvas struct {
|
type Canvas struct {
|
||||||
b backendbase.Backend
|
b backendbase.Backend
|
||||||
|
|
||||||
x, y, w, h int
|
w, h int
|
||||||
fx, fy, fw, fh float64
|
fw, fh float64
|
||||||
|
|
||||||
path Path2D
|
path Path2D
|
||||||
convex bool
|
convex bool
|
||||||
|
@ -30,10 +30,6 @@ type Canvas struct {
|
||||||
state drawState
|
state drawState
|
||||||
stateStack []drawState
|
stateStack []drawState
|
||||||
|
|
||||||
offscreen bool
|
|
||||||
offscrBuf offscreenBuffer
|
|
||||||
offscrImg Image
|
|
||||||
|
|
||||||
images map[interface{}]*Image
|
images map[interface{}]*Image
|
||||||
|
|
||||||
shadowBuf [][2]float64
|
shadowBuf [][2]float64
|
||||||
|
@ -136,16 +132,14 @@ var Performance = struct {
|
||||||
// While all functions on the canvas use the top left point as
|
// While all functions on the canvas use the top left point as
|
||||||
// the origin, since GL uses the bottom left coordinate, the
|
// the origin, since GL uses the bottom left coordinate, the
|
||||||
// coordinates given here also use the bottom left as origin
|
// coordinates given here also use the bottom left as origin
|
||||||
func New(backend backendbase.Backend, x, y, w, h int) *Canvas {
|
func New(backend backendbase.Backend) *Canvas {
|
||||||
if gli == nil {
|
|
||||||
panic("LoadGL must be called before a canvas can be created")
|
|
||||||
}
|
|
||||||
cv := &Canvas{
|
cv := &Canvas{
|
||||||
b: backend,
|
b: backend,
|
||||||
stateStack: make([]drawState, 0, 20),
|
stateStack: make([]drawState, 0, 20),
|
||||||
images: make(map[interface{}]*Image),
|
images: make(map[interface{}]*Image),
|
||||||
}
|
}
|
||||||
cv.SetBounds(x, y, w, h)
|
w, h := backend.Size()
|
||||||
|
cv.setBounds(w, h)
|
||||||
cv.state.lineWidth = 1
|
cv.state.lineWidth = 1
|
||||||
cv.state.lineAlpha = 1
|
cv.state.lineAlpha = 1
|
||||||
cv.state.miterLimitSqr = 100
|
cv.state.miterLimitSqr = 100
|
||||||
|
@ -156,36 +150,9 @@ func New(backend backendbase.Backend, x, y, w, h int) *Canvas {
|
||||||
return cv
|
return cv
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOffscreen creates a new canvas with the given size. It
|
func (cv *Canvas) setBounds(w, h int) {
|
||||||
// does not render directly to the screen but renders to a
|
|
||||||
// texture instead. If alpha is set to true, the offscreen
|
|
||||||
// canvas will have an alpha channel
|
|
||||||
func NewOffscreen(backend backendbase.Backend, w, h int, alpha bool) *Canvas {
|
|
||||||
cv := New(backend, 0, 0, w, h)
|
|
||||||
cv.offscreen = true
|
|
||||||
cv.offscrBuf.alpha = alpha
|
|
||||||
return cv
|
|
||||||
}
|
|
||||||
|
|
||||||
func DeleteOffscreen(cv *Canvas) {
|
|
||||||
if !cv.offscreen {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
gli.DeleteTextures(1, &cv.offscrBuf.tex)
|
|
||||||
gli.DeleteFramebuffers(1, &cv.offscrBuf.frameBuf)
|
|
||||||
gli.DeleteRenderbuffers(1, &cv.offscrBuf.renderStencilBuf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBounds updates the bounds of the canvas. This would
|
|
||||||
// usually be called for example when the window is resized
|
|
||||||
func (cv *Canvas) SetBounds(x, y, w, h int) {
|
|
||||||
if !cv.offscreen {
|
|
||||||
cv.x, cv.y = x, y
|
|
||||||
cv.fx, cv.fy = float64(x), float64(y)
|
|
||||||
}
|
|
||||||
cv.w, cv.h = w, h
|
cv.w, cv.h = w, h
|
||||||
cv.fw, cv.fh = float64(w), float64(h)
|
cv.fw, cv.fh = float64(w), float64(h)
|
||||||
activeCanvas = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Width returns the internal width of the canvas
|
// Width returns the internal width of the canvas
|
||||||
|
@ -202,46 +169,8 @@ func (cv *Canvas) tf(v vec) vec {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// Activate makes the canvas active and sets the viewport. Only needs
|
|
||||||
// to be called if any other GL code changes the viewport
|
|
||||||
func (cv *Canvas) Activate() {
|
|
||||||
if cv.offscreen {
|
|
||||||
gli.Viewport(0, 0, int32(cv.w), int32(cv.h))
|
|
||||||
cv.enableTextureRenderTarget(&cv.offscrBuf)
|
|
||||||
// cv.offscrImg.w = cv.offscrBuf.w
|
|
||||||
// cv.offscrImg.h = cv.offscrBuf.h
|
|
||||||
// cv.offscrImg.tex = cv.offscrBuf.tex
|
|
||||||
} else {
|
|
||||||
gli.Viewport(int32(cv.x), int32(cv.y), int32(cv.w), int32(cv.h))
|
|
||||||
cv.disableTextureRenderTarget()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var activeCanvas *Canvas
|
|
||||||
|
|
||||||
func (cv *Canvas) activate() {
|
|
||||||
if activeCanvas != cv {
|
|
||||||
activeCanvas = cv
|
|
||||||
cv.Activate()
|
|
||||||
}
|
|
||||||
loop:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case f := <-glChan:
|
|
||||||
f()
|
|
||||||
default:
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const alphaTexSize = 2048
|
const alphaTexSize = 2048
|
||||||
|
|
||||||
var (
|
|
||||||
gli GL
|
|
||||||
glChan = make(chan func())
|
|
||||||
)
|
|
||||||
|
|
||||||
type offscreenBuffer struct {
|
type offscreenBuffer struct {
|
||||||
tex uint32
|
tex uint32
|
||||||
w int
|
w int
|
||||||
|
@ -261,23 +190,6 @@ type gaussianShader struct {
|
||||||
kernel int32
|
kernel int32
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadGL needs to be called once per GL context to load the GL assets
|
|
||||||
// that canvas needs. The parameter is an implementation of the GL interface
|
|
||||||
// in this package that should make this package neutral to GL implementations.
|
|
||||||
// The goglimpl subpackage contains an implementation based on Go-GL v3.2
|
|
||||||
func LoadGL(glimpl GL) (err error) {
|
|
||||||
gli = glimpl
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func glError() error {
|
|
||||||
glErr := gli.GetError()
|
|
||||||
if glErr != gl_NO_ERROR {
|
|
||||||
return fmt.Errorf("GL Error: %x", glErr)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetFillStyle sets the color, gradient, or image for any fill calls. To set a
|
// SetFillStyle sets the color, gradient, or image for any fill calls. To set a
|
||||||
// color, there are several acceptable formats: 3 or 4 int values for RGB(A) in
|
// color, there are several acceptable formats: 3 or 4 int values for RGB(A) in
|
||||||
// the range 0-255, 3 or 4 float values for RGB(A) in the range 0-1, hex strings
|
// the range 0-255, 3 or 4 float values for RGB(A) in the range 0-1, hex strings
|
||||||
|
@ -349,57 +261,6 @@ func (cv *Canvas) backendFillStyle(s *drawStyle, alpha float64) backendbase.Fill
|
||||||
return stl
|
return stl
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cv *Canvas) enableTextureRenderTarget(offscr *offscreenBuffer) {
|
|
||||||
if offscr.w != cv.w || offscr.h != cv.h {
|
|
||||||
if offscr.w != 0 && offscr.h != 0 {
|
|
||||||
gli.DeleteTextures(1, &offscr.tex)
|
|
||||||
gli.DeleteFramebuffers(1, &offscr.frameBuf)
|
|
||||||
gli.DeleteRenderbuffers(1, &offscr.renderStencilBuf)
|
|
||||||
}
|
|
||||||
offscr.w = cv.w
|
|
||||||
offscr.h = cv.h
|
|
||||||
|
|
||||||
gli.ActiveTexture(gl_TEXTURE0)
|
|
||||||
gli.GenTextures(1, &offscr.tex)
|
|
||||||
gli.BindTexture(gl_TEXTURE_2D, offscr.tex)
|
|
||||||
// todo do non-power-of-two textures work everywhere?
|
|
||||||
if offscr.alpha {
|
|
||||||
gli.TexImage2D(gl_TEXTURE_2D, 0, gl_RGBA, int32(cv.w), int32(cv.h), 0, gl_RGBA, gl_UNSIGNED_BYTE, nil)
|
|
||||||
} else {
|
|
||||||
gli.TexImage2D(gl_TEXTURE_2D, 0, gl_RGB, int32(cv.w), int32(cv.h), 0, gl_RGB, gl_UNSIGNED_BYTE, nil)
|
|
||||||
}
|
|
||||||
gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, gl_NEAREST)
|
|
||||||
gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, gl_NEAREST)
|
|
||||||
|
|
||||||
gli.GenFramebuffers(1, &offscr.frameBuf)
|
|
||||||
gli.BindFramebuffer(gl_FRAMEBUFFER, offscr.frameBuf)
|
|
||||||
|
|
||||||
gli.GenRenderbuffers(1, &offscr.renderStencilBuf)
|
|
||||||
gli.BindRenderbuffer(gl_RENDERBUFFER, offscr.renderStencilBuf)
|
|
||||||
gli.RenderbufferStorage(gl_RENDERBUFFER, gl_DEPTH24_STENCIL8, int32(cv.w), int32(cv.h))
|
|
||||||
gli.FramebufferRenderbuffer(gl_FRAMEBUFFER, gl_DEPTH_STENCIL_ATTACHMENT, gl_RENDERBUFFER, offscr.renderStencilBuf)
|
|
||||||
|
|
||||||
gli.FramebufferTexture(gl_FRAMEBUFFER, gl_COLOR_ATTACHMENT0, offscr.tex, 0)
|
|
||||||
|
|
||||||
if err := gli.CheckFramebufferStatus(gl_FRAMEBUFFER); err != gl_FRAMEBUFFER_COMPLETE {
|
|
||||||
// todo this should maybe not panic
|
|
||||||
panic(fmt.Sprintf("Failed to set up framebuffer for offscreen texture: %x", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
gli.Clear(gl_COLOR_BUFFER_BIT | gl_STENCIL_BUFFER_BIT)
|
|
||||||
} else {
|
|
||||||
gli.BindFramebuffer(gl_FRAMEBUFFER, offscr.frameBuf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cv *Canvas) disableTextureRenderTarget() {
|
|
||||||
if cv.offscreen {
|
|
||||||
cv.enableTextureRenderTarget(&cv.offscrBuf)
|
|
||||||
} else {
|
|
||||||
gli.BindFramebuffer(gl_FRAMEBUFFER, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetLineWidth sets the line width for any line drawing calls
|
// SetLineWidth sets the line width for any line drawing calls
|
||||||
func (cv *Canvas) SetLineWidth(width float64) {
|
func (cv *Canvas) SetLineWidth(width float64) {
|
||||||
if width < 0 {
|
if width < 0 {
|
||||||
|
@ -430,7 +291,7 @@ func (cv *Canvas) SetFont(src interface{}, size float64) {
|
||||||
if f, ok := fonts[v]; ok {
|
if f, ok := fonts[v]; ok {
|
||||||
cv.state.font = f
|
cv.state.font = f
|
||||||
} else {
|
} else {
|
||||||
f, err := LoadFont(v)
|
f, err := cv.LoadFont(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error loading font %s: %v\n", v, err)
|
fmt.Fprintf(os.Stderr, "Error loading font %s: %v\n", v, err)
|
||||||
fonts[v] = nil
|
fonts[v] = nil
|
||||||
|
|
22
images.go
22
images.go
|
@ -124,13 +124,13 @@ func (img *Image) Replace(src interface{}) error {
|
||||||
// source coordinates
|
// source coordinates
|
||||||
func (cv *Canvas) DrawImage(image interface{}, coords ...float64) {
|
func (cv *Canvas) DrawImage(image interface{}, coords ...float64) {
|
||||||
var img *Image
|
var img *Image
|
||||||
var flip bool
|
// var flip bool
|
||||||
if cv2, ok := image.(*Canvas); ok && cv2.offscreen {
|
// if cv2, ok := image.(*Canvas); ok && cv2.offscreen {
|
||||||
img = &cv2.offscrImg
|
// img = &cv2.offscrImg
|
||||||
flip = true
|
// flip = true
|
||||||
} else {
|
// } else {
|
||||||
img = cv.getImage(image)
|
img = cv.getImage(image)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if img == nil {
|
if img == nil {
|
||||||
return
|
return
|
||||||
|
@ -140,8 +140,6 @@ func (cv *Canvas) DrawImage(image interface{}, coords ...float64) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
cv.activate()
|
|
||||||
|
|
||||||
var sx, sy, sw, sh, dx, dy, dw, dh float64
|
var sx, sy, sw, sh, dx, dy, dw, dh float64
|
||||||
sw, sh = float64(img.Width()), float64(img.Height())
|
sw, sh = float64(img.Width()), float64(img.Height())
|
||||||
dw, dh = float64(img.Width()), float64(img.Height())
|
dw, dh = float64(img.Width()), float64(img.Height())
|
||||||
|
@ -157,10 +155,10 @@ func (cv *Canvas) DrawImage(image interface{}, coords ...float64) {
|
||||||
dw, dh = coords[6], coords[7]
|
dw, dh = coords[6], coords[7]
|
||||||
}
|
}
|
||||||
|
|
||||||
if flip {
|
// if flip {
|
||||||
dy += dh
|
// dy += dh
|
||||||
dh = -dh
|
// dh = -dh
|
||||||
}
|
// }
|
||||||
|
|
||||||
var data [4][2]float64
|
var data [4][2]float64
|
||||||
data[0] = cv.tf(vec{dx, dy})
|
data[0] = cv.tf(vec{dx, dy})
|
||||||
|
|
7
paths.go
7
paths.go
|
@ -96,8 +96,6 @@ func (cv *Canvas) strokePath(path *Path2D) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
cv.activate()
|
|
||||||
|
|
||||||
dashedPath := cv.applyLineDash(path.p)
|
dashedPath := cv.applyLineDash(path.p)
|
||||||
|
|
||||||
var triBuf [500][2]float64
|
var triBuf [500][2]float64
|
||||||
|
@ -328,7 +326,6 @@ func (cv *Canvas) FillPath(path *Path2D) {
|
||||||
if len(path.p) < 3 {
|
if len(path.p) < 3 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cv.activate()
|
|
||||||
|
|
||||||
var triBuf [500][2]float64
|
var triBuf [500][2]float64
|
||||||
tris := buildFillTriangles(path, triBuf[:0])
|
tris := buildFillTriangles(path, triBuf[:0])
|
||||||
|
@ -431,8 +428,6 @@ func (cv *Canvas) StrokeRect(x, y, w, h float64) {
|
||||||
|
|
||||||
// FillRect fills a rectangle with the active fill style
|
// FillRect fills a rectangle with the active fill style
|
||||||
func (cv *Canvas) FillRect(x, y, w, h float64) {
|
func (cv *Canvas) FillRect(x, y, w, h float64) {
|
||||||
cv.activate()
|
|
||||||
|
|
||||||
p0 := cv.tf(vec{x, y})
|
p0 := cv.tf(vec{x, y})
|
||||||
p1 := cv.tf(vec{x, y + h})
|
p1 := cv.tf(vec{x, y + h})
|
||||||
p2 := cv.tf(vec{x + w, y + h})
|
p2 := cv.tf(vec{x + w, y + h})
|
||||||
|
@ -448,8 +443,6 @@ func (cv *Canvas) FillRect(x, y, w, h float64) {
|
||||||
|
|
||||||
// ClearRect sets the color of the rectangle to transparent black
|
// ClearRect sets the color of the rectangle to transparent black
|
||||||
func (cv *Canvas) ClearRect(x, y, w, h float64) {
|
func (cv *Canvas) ClearRect(x, y, w, h float64) {
|
||||||
cv.activate()
|
|
||||||
|
|
||||||
p0 := cv.tf(vec{x, y})
|
p0 := cv.tf(vec{x, y})
|
||||||
p1 := cv.tf(vec{x, y + h})
|
p1 := cv.tf(vec{x, y + h})
|
||||||
p2 := cv.tf(vec{x + w, y + h})
|
p2 := cv.tf(vec{x + w, y + h})
|
||||||
|
|
|
@ -12,9 +12,7 @@ import (
|
||||||
|
|
||||||
"github.com/go-gl/gl/v3.2-core/gl"
|
"github.com/go-gl/gl/v3.2-core/gl"
|
||||||
"github.com/tfriedel6/canvas"
|
"github.com/tfriedel6/canvas"
|
||||||
"github.com/tfriedel6/canvas/backend/backendbase"
|
|
||||||
"github.com/tfriedel6/canvas/backend/gogl"
|
"github.com/tfriedel6/canvas/backend/gogl"
|
||||||
"github.com/tfriedel6/canvas/glimpl/gogl"
|
|
||||||
"github.com/veandco/go-sdl2/sdl"
|
"github.com/veandco/go-sdl2/sdl"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,7 +22,7 @@ type Window struct {
|
||||||
Window *sdl.Window
|
Window *sdl.Window
|
||||||
WindowID uint32
|
WindowID uint32
|
||||||
GLContext sdl.GLContext
|
GLContext sdl.GLContext
|
||||||
Backend backendbase.Backend
|
Backend *goglbackend.GoGLBackend
|
||||||
canvas *canvas.Canvas
|
canvas *canvas.Canvas
|
||||||
frameTimes [10]time.Time
|
frameTimes [10]time.Time
|
||||||
frameIndex int
|
frameIndex int
|
||||||
|
@ -98,12 +96,7 @@ func CreateWindow(w, h int, title string) (*Window, *canvas.Canvas, error) {
|
||||||
sdl.GLSetSwapInterval(1)
|
sdl.GLSetSwapInterval(1)
|
||||||
gl.Enable(gl.MULTISAMPLE)
|
gl.Enable(gl.MULTISAMPLE)
|
||||||
|
|
||||||
err = canvas.LoadGL(glimplgogl.GLImpl{})
|
cv := canvas.New(backend)
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("Error loading canvas GL assets: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cv := canvas.New(backend, 0, 0, w, h)
|
|
||||||
wnd := &Window{
|
wnd := &Window{
|
||||||
Window: window,
|
Window: window,
|
||||||
WindowID: windowID,
|
WindowID: windowID,
|
||||||
|
@ -195,7 +188,7 @@ func (wnd *Window) StartFrame() error {
|
||||||
wnd.SizeChange(int(e.Data1), int(e.Data2))
|
wnd.SizeChange(int(e.Data1), int(e.Data2))
|
||||||
handled = true
|
handled = true
|
||||||
} else {
|
} else {
|
||||||
wnd.canvas.SetBounds(0, 0, int(e.Data1), int(e.Data2))
|
wnd.Backend.SetBounds(0, 0, int(e.Data1), int(e.Data2))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
9
text.go
9
text.go
|
@ -28,10 +28,7 @@ var defaultFont *Font
|
||||||
|
|
||||||
// LoadFont loads a font and returns the result. The font
|
// LoadFont loads a font and returns the result. The font
|
||||||
// can be a file name or a byte slice in TTF format
|
// can be a file name or a byte slice in TTF format
|
||||||
func LoadFont(src interface{}) (*Font, error) {
|
func (cv *Canvas) LoadFont(src interface{}) (*Font, error) {
|
||||||
if gli == nil {
|
|
||||||
panic("LoadGL must be called before fonts can be loaded")
|
|
||||||
}
|
|
||||||
var f *Font
|
var f *Font
|
||||||
switch v := src.(type) {
|
switch v := src.(type) {
|
||||||
case *truetype.Font:
|
case *truetype.Font:
|
||||||
|
@ -64,8 +61,6 @@ func LoadFont(src interface{}) (*Font, error) {
|
||||||
// FillText draws the given string at the given coordinates
|
// FillText draws the given string at the given coordinates
|
||||||
// using the currently set font and font height
|
// using the currently set font and font height
|
||||||
func (cv *Canvas) FillText(str string, x, y float64) {
|
func (cv *Canvas) FillText(str string, x, y float64) {
|
||||||
cv.activate()
|
|
||||||
|
|
||||||
if cv.state.font == nil {
|
if cv.state.font == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -227,8 +222,6 @@ func (cv *Canvas) FillText(str string, x, y float64) {
|
||||||
// using the currently set font and font height and using the
|
// using the currently set font and font height and using the
|
||||||
// current stroke style
|
// current stroke style
|
||||||
func (cv *Canvas) StrokeText(str string, x, y float64) {
|
func (cv *Canvas) StrokeText(str string, x, y float64) {
|
||||||
cv.activate()
|
|
||||||
|
|
||||||
if cv.state.font == nil {
|
if cv.state.font == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue