114 lines
2.9 KiB
Go
114 lines
2.9 KiB
Go
package xcanvas
|
|
|
|
import "image"
|
|
import "image/color"
|
|
import "github.com/jezek/xgbutil"
|
|
import "github.com/jezek/xgb/xproto"
|
|
import "github.com/jezek/xgbutil/xgraphics"
|
|
import "git.tebibyte.media/tomo/tomo/canvas"
|
|
|
|
// Canvas satisfies the canvas.Canvas interface. It draws to an xgraphics.Image.
|
|
// It must be closed after use.
|
|
type Canvas struct {
|
|
*xgraphics.Image
|
|
}
|
|
|
|
// NewCanvas creates a new canvas from a bounding rectangle.
|
|
func NewCanvas (x *xgbutil.XUtil, bounds image.Rectangle) *Canvas {
|
|
return NewCanvasFrom(xgraphics.New(x, bounds))
|
|
}
|
|
|
|
// NewCanvasFrom creates a new canvas from an existing xgraphics.Image. Note
|
|
// that calling Close() on the resulting canvas will destroy this image.
|
|
func NewCanvasFrom (image *xgraphics.Image) *Canvas {
|
|
if image == nil { return nil }
|
|
return &Canvas { image }
|
|
}
|
|
|
|
// Pen returns a new drawing context.
|
|
func (this *Canvas) Pen () canvas.Pen {
|
|
return &pen {
|
|
image: this.Image,
|
|
}
|
|
}
|
|
|
|
// Clip returns a sub-canvas of this canvas.
|
|
func (this *Canvas) Clip (bounds image.Rectangle) canvas.Canvas {
|
|
this.assert()
|
|
subImage := this.Image.SubImage(bounds)
|
|
if subImage == nil { return nil }
|
|
xImage := subImage.(*xgraphics.Image)
|
|
return &Canvas { xImage }
|
|
}
|
|
|
|
// Push pushes this canvas to the screen.
|
|
func (this *Canvas) Push (window xproto.Window) {
|
|
this.assert()
|
|
this.XDraw()
|
|
this.XExpPaint(window, this.Bounds().Min.X, this.Bounds().Min.Y)
|
|
}
|
|
|
|
// Close frees this canvas from the X server.
|
|
func (this *Canvas) Close () {
|
|
this.assert()
|
|
this.Image.Destroy()
|
|
}
|
|
|
|
func (this *Canvas) assert () {
|
|
if this == nil { panic("nil canvas") }
|
|
}
|
|
|
|
// TODO: we need to implement:
|
|
// - cap
|
|
// - joint
|
|
// - align
|
|
|
|
type pen struct {
|
|
image *xgraphics.Image
|
|
|
|
closed bool
|
|
endCap canvas.Cap
|
|
joint canvas.Joint
|
|
weight int
|
|
align canvas.StrokeAlign
|
|
stroke xgraphics.BGRA
|
|
fill xgraphics.BGRA
|
|
}
|
|
|
|
func (this *pen) Rectangle (bounds image.Rectangle) {
|
|
if this.weight == 0 {
|
|
this.gfx.fillRectangle(bounds)
|
|
} else {
|
|
this.gfx.strokeRectangle(bounds)
|
|
}
|
|
}
|
|
|
|
func (this *pen) Path (points ...image.Point) {
|
|
if this.weight == 0 {
|
|
this.fillPolygon(points...)
|
|
} else if this.closed {
|
|
this.strokePolygon(points...)
|
|
} else {
|
|
this.polyLine(points...)
|
|
}
|
|
}
|
|
|
|
func (this *pen) Closed (closed bool) { this.closed = closed }
|
|
func (this *pen) Cap (endCap canvas.Cap) { this.endCap = endCap }
|
|
func (this *pen) Joint (joint canvas.Joint) { this.joint = joint }
|
|
func (this *pen) StrokeWeight (weight int) { this.weight = weight }
|
|
func (this *pen) StrokeAlign (align canvas.StrokeAlign) { this.align = align }
|
|
|
|
func (this *pen) Stroke (stroke color.Color) { this.stroke = convertColor(stroke) }
|
|
func (this *pen) Fill (fill color.Color) { this.fill = convertColor(fill) }
|
|
|
|
func convertColor (c color.Color) xgraphics.BGRA {
|
|
r, g, b, a := c.RGBA()
|
|
return xgraphics.BGRA {
|
|
B: uint8(b >> 8),
|
|
G: uint8(g >> 8),
|
|
R: uint8(r >> 8),
|
|
A: uint8(a >> 8),
|
|
}
|
|
}
|