This repository has been archived on 2024-06-02. You can view files and clone it, but cannot push or open issues or pull requests.
x/canvas/canvas.go

114 lines
2.9 KiB
Go
Raw Normal View History

2023-07-02 00:52:14 -06:00
package xcanvas
import "image"
import "image/color"
import "github.com/jezek/xgbutil"
2023-07-03 22:04:00 -06:00
import "github.com/jezek/xgb/xproto"
2023-07-02 00:52:14 -06:00
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.
2023-07-04 22:44:56 -06:00
func New (x *xgbutil.XUtil, bounds image.Rectangle) *Canvas {
return NewFrom(xgraphics.New(x, bounds))
2023-07-02 00:52:14 -06:00
}
// NewFrom creates a new canvas from an existing xgraphics.Image.
2023-07-04 22:44:56 -06:00
func NewFrom (image *xgraphics.Image) *Canvas {
if image == nil { return nil }
return &Canvas { image }
2023-07-02 00:52:14 -06:00
}
// Pen returns a new drawing context.
2023-07-04 22:44:56 -06:00
func (this *Canvas) Pen () canvas.Pen {
return &pen {
2023-07-02 00:52:14 -06:00
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.
2023-07-04 22:44:56 -06:00
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 }
2023-07-02 00:52:14 -06:00
}
2023-07-03 22:04:00 -06:00
// Push pushes this canvas to the screen.
2023-07-04 22:44:56 -06:00
func (this *Canvas) Push (window xproto.Window) {
this.assert()
2023-07-03 22:04:00 -06:00
this.XDraw()
this.XExpPaint(window, this.Bounds().Min.X, this.Bounds().Min.Y)
}
2023-07-04 22:44:56 -06:00
func (this *Canvas) assert () {
if this == nil { panic("nil canvas") }
}
2023-07-02 00:52:14 -06:00
// 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
}
2023-07-04 22:44:56 -06:00
func (this *pen) Rectangle (bounds image.Rectangle) {
2023-07-02 00:52:14 -06:00
if this.weight == 0 {
this.gfx.FillRectangle(this.fill[:], bounds)
} else {
this.gfx.StrokeRectangle(this.stroke[:], this.weight, bounds)
}
}
2023-07-04 22:44:56 -06:00
func (this *pen) Path (points ...image.Point) {
2023-07-02 00:52:14 -06:00
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...)
}
}
2023-07-04 22:44:56 -06:00
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 }
2023-07-02 00:52:14 -06:00
2023-07-04 22:44:56 -06:00
func (this *pen) Stroke (stroke color.Color) { this.stroke = convertColor(stroke) }
func (this *pen) Fill (fill color.Color) { this.fill = convertColor(fill) }
2023-07-02 00:52:14 -06:00
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),
2023-07-04 22:44:56 -06:00
uint8(a >> 8),
2023-07-02 00:52:14 -06:00
}
}