package xcanvas import "image" import "image/color" import "github.com/jezek/xgbutil" import "github.com/jezek/xgb/xproto" import "git.tebibyte.media/tomo/ggfx" 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. type Canvas struct { *xgraphics.Image } // New creates a new canvas from a bounding rectangle. func New (x *xgbutil.XUtil, bounds image.Rectangle) *Canvas { return NewFrom(xgraphics.New(x, bounds)) } // NewFrom creates a new canvas from an existing xgraphics.Image. func NewFrom (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, gfx: ggfx.Image[uint8] { Pix: this.Image.Pix, Stride: this.Image.Stride, Bounds: this.Image.Rect, Width: 4, }, } } // 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) } func (this *Canvas) assert () { if this == nil { panic("nil canvas") } } // TODO: we need to implement: // - cap // - joint // - align type pen struct { image *xgraphics.Image gfx ggfx.Image[uint8] closed bool endCap canvas.Cap joint canvas.Joint weight int align canvas.StrokeAlign stroke [4]uint8 fill [4]uint8 } func (this *pen) Rectangle (bounds image.Rectangle) { if this.weight == 0 { this.gfx.FillRectangle(this.fill[:], bounds) } else { this.gfx.StrokeRectangle(this.stroke[:], this.weight, bounds) } } func (this *pen) Path (points ...image.Point) { if this.weight == 0 { this.gfx.FillPolygon(this.fill[:], points...) } else if this.closed { this.gfx.StrokePolygon(this.stroke[:], this.weight, points...) } else { this.gfx.PolyLine(this.stroke[:], this.weight, 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) [4]uint8 { r, g, b, a := c.RGBA() return [4]uint8 { uint8(b >> 8), uint8(g >> 8), uint8(r >> 8), uint8(a >> 8), } }