moved offscreen canvas to backend

This commit is contained in:
Thomas Friedel 2019-02-22 12:34:08 +01:00
parent a0dfb6f530
commit 8dc91b34f5
12 changed files with 122 additions and 211 deletions

View file

@ -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

View file

@ -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,

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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 {

View file

@ -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
View file

@ -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

View file

@ -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})

View file

@ -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})

View file

@ -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))
} }
} }
} }

View file

@ -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
} }