diff --git a/canvas.go b/canvas.go index 6eb7694..fa75a5d 100644 --- a/canvas.go +++ b/canvas.go @@ -14,25 +14,41 @@ type Canvas struct { x, y, w, h int fx, fy, fw, fh float32 - fill struct { + path []pathPoint + text struct { + target *image.RGBA + tex uint32 + } + + state drawState + stateStack []drawState +} + +type pathPoint struct { + pos lm.Vec2 + move bool +} + +type drawState struct { + transform lm.Mat3x3 + fill struct { r, g, b, a float32 } stroke struct { r, g, b, a float32 lineWidth float32 } - path []pathPoint - text struct { - font *Font - size float32 - target *image.RGBA - tex uint32 - } -} - -type pathPoint struct { - pos lm.Vec2 - move bool + font *Font + fontSize float32 + /* + The current transformation matrix. + The current clipping region. + The current dash list. + The current values of the following attributes: strokeStyle, fillStyle, globalAlpha, + lineWidth, lineCap, lineJoin, miterLimit, lineDashOffset, shadowOffsetX, + shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation, font, + textAlign, textBaseline, direction, imageSmoothingEnabled + */ } // New creates a new canvas with the given viewport coordinates. @@ -44,8 +60,9 @@ func New(x, y, w, h int) *Canvas { x: x, y: y, w: w, h: h, fx: float32(x), fy: float32(y), fw: float32(w), fh: float32(h), + stateStack: make([]drawState, 0, 20), } - cv.stroke.lineWidth = 1 + cv.state.stroke.lineWidth = 1 return cv } @@ -157,7 +174,8 @@ func glError() error { func (cv *Canvas) SetFillColor(value ...interface{}) { r, g, b, a, ok := parseColor(value...) if ok { - cv.fill.r, cv.fill.g, cv.fill.b, cv.fill.a = r, g, b, a + f := &cv.state.fill + f.r, f.g, f.b, f.a = r, g, b, a } } @@ -165,19 +183,35 @@ func (cv *Canvas) SetFillColor(value ...interface{}) { func (cv *Canvas) SetStrokeColor(value ...interface{}) { r, g, b, a, ok := parseColor(value...) if ok { - cv.stroke.r, cv.stroke.g, cv.stroke.b, cv.stroke.a = r, g, b, a + s := &cv.state.stroke + s.r, s.g, s.b, s.a = r, g, b, a } } // SetLineWidth sets the line width for any line drawing calls func (cv *Canvas) SetLineWidth(width float32) { - cv.stroke.lineWidth = width + cv.state.stroke.lineWidth = width } // SetFont sets the font and font size func (cv *Canvas) SetFont(font *Font, size float32) { - cv.text.font = font - cv.text.size = size + cv.state.font = font + cv.state.fontSize = size +} + +// Save saves the current draw state to a stack +func (cv *Canvas) Save() { + cv.stateStack = append(cv.stateStack, cv.state) +} + +// Restore restores the last draw state from the stack if available +func (cv *Canvas) Restore() { + l := len(cv.stateStack) + if l <= 0 { + return + } + cv.state = cv.stateStack[l-1] + cv.stateStack = cv.stateStack[:l-1] } // FillRect fills a rectangle with the active color @@ -194,7 +228,8 @@ func (cv *Canvas) FillRect(x, y, w, h float32) { gli.BufferData(gl_ARRAY_BUFFER, len(data)*4, unsafe.Pointer(&data[0]), gl_STREAM_DRAW) gli.VertexAttribPointer(sr.vertex, 2, gl_FLOAT, false, 0, nil) - gli.Uniform4f(sr.color, cv.fill.r, cv.fill.g, cv.fill.b, cv.fill.a) + f := cv.state.fill + gli.Uniform4f(sr.color, f.r, f.g, f.b, f.a) gli.EnableVertexAttribArray(sr.vertex) gli.DrawArrays(gl_TRIANGLE_FAN, 0, 4) gli.DisableVertexAttribArray(sr.vertex) diff --git a/paths.go b/paths.go index 8cac1d4..478c556 100644 --- a/paths.go +++ b/paths.go @@ -69,7 +69,8 @@ func (cv *Canvas) Stroke() { gli.Clear(gl_STENCIL_BUFFER_BIT) gli.UseProgram(sr.id) - gli.Uniform4f(sr.color, cv.stroke.r, cv.stroke.g, cv.stroke.b, cv.stroke.a) + s := cv.state.stroke + gli.Uniform4f(sr.color, s.r, s.g, s.b, s.a) gli.EnableVertexAttribArray(sr.vertex) gli.BindBuffer(gl_ARRAY_BUFFER, buf) @@ -86,8 +87,8 @@ func (cv *Canvas) Stroke() { p1 := p.pos v1 := p1.Sub(p0).Norm() - v2 := lm.Vec2{v1[1], -v1[0]}.MulF(cv.stroke.lineWidth * 0.5) - v1 = v1.MulF(cv.stroke.lineWidth * 0.5) + v2 := lm.Vec2{v1[1], -v1[0]}.MulF(cv.state.stroke.lineWidth * 0.5) + v1 = v1.MulF(cv.state.stroke.lineWidth * 0.5) x0f, y0f := cv.vecToGL(p0.Sub(v1).Add(v2)) x1f, y1f := cv.vecToGL(p1.Add(v1).Add(v2)) @@ -122,7 +123,8 @@ func (cv *Canvas) Fill() { cv.activate() gli.UseProgram(sr.id) - gli.Uniform4f(sr.color, cv.fill.r, cv.fill.g, cv.fill.b, cv.fill.a) + f := cv.state.fill + gli.Uniform4f(sr.color, f.r, f.g, f.b, f.a) gli.EnableVertexAttribArray(sr.vertex) gli.BindBuffer(gl_ARRAY_BUFFER, buf) diff --git a/text.go b/text.go index 65b47c3..afd8337 100644 --- a/text.go +++ b/text.go @@ -59,9 +59,10 @@ func (cv *Canvas) FillText(str string, x, y float32) { gli.TexImage2D(gl_TEXTURE_2D, 0, gl_RGBA, int32(cv.w), int32(cv.h), 0, gl_RGBA, gl_UNSIGNED_BYTE, nil) } - fontRenderingContext.setFont(cv.text.font.font) - fontRenderingContext.setFontSize(float64(cv.text.size)) - fontRenderingContext.setSrc(image.NewUniform(colorGLToGo(cv.fill.r, cv.fill.g, cv.fill.b, cv.fill.a))) + fontRenderingContext.setFont(cv.state.font.font) + fontRenderingContext.setFontSize(float64(cv.state.fontSize)) + f := cv.state.fill + fontRenderingContext.setSrc(image.NewUniform(colorGLToGo(f.r, f.g, f.b, f.a))) fontRenderingContext.setDst(cv.text.target) fontRenderingContext.setClip(cv.text.target.Bounds()) _, bounds, _ := fontRenderingContext.drawString(str, fixed.Point26_6{X: fixed.Int26_6(x*64 + 0.5), Y: fixed.Int26_6(y*64 + 0.5)})