diff --git a/box.go b/box.go index 9c94dfe..491e52c 100644 --- a/box.go +++ b/box.go @@ -282,7 +282,7 @@ func (this *box) Draw (can canvas.Canvas) { if can == nil { return } pen := can.Pen() pen.Fill(this.color) - if this.texture == nil || this.texture.transparent { + if this.texture == nil || !this.texture.Opaque() { pen.Rectangle(this.bounds) } if this.texture != nil { @@ -431,7 +431,7 @@ func (this *box) determineFillTransparency () { _, _, _, a := this.color.RGBA() this.fillTransparent = a != 0xFFFF && - !(this.texture != nil && !this.texture.transparent) + !(this.texture != nil && this.texture.Opaque()) } func (this *box) propagate (callback func (anyBox) bool) bool { diff --git a/canvas/canvas.go b/canvas/canvas.go index f72433b..b74c06f 100644 --- a/canvas/canvas.go +++ b/canvas/canvas.go @@ -4,22 +4,23 @@ 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. +// It must be closed after use. 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)) +// NewCanvas creates a new canvas from a bounding rectangle. +func NewCanvas (x *xgbutil.XUtil, bounds image.Rectangle) *Canvas { + return NewCanvasFrom(xgraphics.New(x, bounds)) } -// NewFrom creates a new canvas from an existing xgraphics.Image. -func NewFrom (image *xgraphics.Image) *Canvas { +// 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 } } @@ -28,12 +29,6 @@ func NewFrom (image *xgraphics.Image) *Canvas { func (this *Canvas) Pen () canvas.Pen { return &pen { image: this.Image, - gfx: ggfx.Image[uint8] { - Pix: this.Image.Pix, - Stride: this.Image.Stride, - Rect: this.Image.Rect, - Width: 4, - }, } } @@ -53,6 +48,12 @@ func (this *Canvas) Push (window xproto.Window) { 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") } } @@ -64,35 +65,34 @@ func (this *Canvas) assert () { 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 + stroke xgraphics.BGRA + fill xgraphics.BGRA } func (this *pen) Rectangle (bounds image.Rectangle) { if this.weight == 0 { - this.gfx.FillRectangle(this.fill[:], bounds) + this.gfx.fillRectangle(bounds) } else { - this.gfx.StrokeRectangle(this.stroke[:], this.weight, bounds) + this.gfx.strokeRectangle(bounds) } } func (this *pen) Path (points ...image.Point) { if this.weight == 0 { - this.gfx.FillPolygon(this.fill[:], points...) + this.fillPolygon(points...) } else if this.closed { - this.gfx.StrokePolygon(this.stroke[:], this.weight, points...) + this.strokePolygon(points...) } else { - this.gfx.PolyLine(this.stroke[:], this.weight, points...) + 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 } @@ -100,14 +100,14 @@ 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 (this *pen) Fill (fill color.Color) { this.fill = convertColor(fill) } -func convertColor (c color.Color) [4]uint8 { +func convertColor (c color.Color) xgraphics.BGRA { r, g, b, a := c.RGBA() - return [4]uint8 { - uint8(b >> 8), - uint8(g >> 8), - uint8(r >> 8), - uint8(a >> 8), + return xgraphics.BGRA { + B: uint8(b >> 8), + G: uint8(g >> 8), + R: uint8(r >> 8), + A: uint8(a >> 8), } } diff --git a/canvas/draw.go b/canvas/draw.go new file mode 100644 index 0000000..04c94bf --- /dev/null +++ b/canvas/draw.go @@ -0,0 +1,5 @@ +package xcanvas + +func (this *pen) fillRectangle (bounds image.Rectangle) { + +} diff --git a/canvas/texture.go b/canvas/texture.go new file mode 100644 index 0000000..dec80a1 --- /dev/null +++ b/canvas/texture.go @@ -0,0 +1,60 @@ +package xcanvas + +import "image" + +// Texture is a read-only image texture that can be quickly written to a canvas. +// It must be closed manually after use. +type Texture struct { + pix []uint8 + stride int + rect image.Rectangle + transparent bool +} + +// NewTextureFrom creates a new texture from a source image. +func NewTextureFrom (source image.Image) *Texture { + bounds := source.Bounds() + texture := &Texture { + pix: make([]uint8, bounds.Dx() * bounds.Dy() * 4), + stride: bounds.Dx(), + rect: bounds.Sub(bounds.Min), + } + + index := 0 + var point image.Point + for point.Y = bounds.Min.Y; point.Y < bounds.Max.Y; point.Y ++ { + for point.X = bounds.Min.X; point.X < bounds.Max.X; point.X ++ { + r, g, b, a := source.At(point.X, point.Y).RGBA() + texture.pix[index + 0] = uint8(b >> 8) + texture.pix[index + 1] = uint8(g >> 8) + texture.pix[index + 2] = uint8(r >> 8) + texture.pix[index + 3] = uint8(a >> 8) + index += 4 + + if a != 0xFFFF { + texture.transparent = true + } + }} + + return texture +} + +// Opaque reports whether or not the texture is fully opaque. +func (this *Texture) Opaque () bool { + return !this.transparent +} + +// Close frees the texture from memory. +func (this *Texture) Close () error { + // i lied we dont actually need to close this, but we will once this + // texture resides on the x server or in video memory. + return nil +} + +// Clip returns a subset of this texture that points to the same data. +func (this *Texture) Clip (bounds image.Rectangle) *Texture { + clipped := *this + clipped.rect = bounds + return &clipped +} + diff --git a/containerbox.go b/containerbox.go index a31a0e8..5d8937f 100644 --- a/containerbox.go +++ b/containerbox.go @@ -143,7 +143,7 @@ func (this *containerBox) Draw (can canvas.Canvas) { if this.fillTransparent && this.parent != nil { this.parent.drawBackgroundPart(can.Clip(tile)) } - if this.texture == nil || this.texture.transparent { + if this.texture == nil || !this.texture.Opaque() { pen.Rectangle(tile) } if this.texture != nil { @@ -162,7 +162,7 @@ func (this *containerBox) drawBackgroundPart (can canvas.Canvas) { pen := can.Pen() pen.Fill(this.color) - if this.texture == nil || this.texture.transparent { + if this.texture == nil || !this.texture.Opaque() { pen.Rectangle(can.Bounds()) } if this.texture != nil { diff --git a/texture.go b/texture.go index 03264bf..6faa883 100644 --- a/texture.go +++ b/texture.go @@ -2,49 +2,22 @@ package x import "image" import "git.tebibyte.media/tomo/tomo" +import "git.tebibyte.media/tomo/x/canvas" type texture struct { - pix []uint8 - stride int - rect image.Rectangle - transparent bool + *xcanvas.Texture } -func (backend *Backend) NewTexture (source image.Image) tomo.Texture { - bounds := source.Bounds() - texture := &texture { - pix: make([]uint8, bounds.Dx() * bounds.Dy() * 4), - stride: bounds.Dx(), - rect: bounds.Sub(bounds.Min), +func (backend Backend) NewTexture (source image.Image) tomo.Texture { + return texture { + Texture: xcanvas.NewTextureFrom(source), } - - index := 0 - var point image.Point - for point.Y = bounds.Min.Y; point.Y < bounds.Max.Y; point.Y ++ { - for point.X = bounds.Min.X; point.X < bounds.Max.X; point.X ++ { - r, g, b, a := source.At(point.X, point.Y).RGBA() - texture.pix[index + 0] = uint8(b >> 8) - texture.pix[index + 1] = uint8(g >> 8) - texture.pix[index + 2] = uint8(r >> 8) - texture.pix[index + 3] = uint8(a >> 8) - index += 4 - - if a != 0xFFFF { - texture.transparent = true - } - }} - - return texture } -func (this *texture) Clip (bounds image.Rectangle) tomo.Texture { - clipped := *this - clipped.rect = bounds - return &clipped -} - -func (this *texture) Close () error { - return nil +func (this texture) Clip (bounds image.Rectangle) tomo.Texture { + return texture { + Texture: this.Texture.Clip(bounds), + } } func assertTexture (unknown tomo.Texture) *texture { diff --git a/window.go b/window.go index 72f6854..288e571 100644 --- a/window.go +++ b/window.go @@ -332,7 +332,7 @@ func (window *window) reallocateCanvas () { if window.xCanvas != nil { window.xCanvas.Destroy() } - window.xCanvas = xcanvas.NewFrom(xgraphics.New ( + window.xCanvas = xcanvas.NewCanvasFrom(xgraphics.New ( window.backend.x, image.Rect ( 0, 0,