WIP moving ggfx stuff into xcanvas

This commit is contained in:
Sasha Koshka 2023-08-22 21:24:38 -04:00
parent 8401b5d0f9
commit 2f0259d913
7 changed files with 107 additions and 69 deletions

4
box.go
View File

@ -282,7 +282,7 @@ func (this *box) Draw (can canvas.Canvas) {
if can == nil { return } if can == nil { return }
pen := can.Pen() pen := can.Pen()
pen.Fill(this.color) pen.Fill(this.color)
if this.texture == nil || this.texture.transparent { if this.texture == nil || !this.texture.Opaque() {
pen.Rectangle(this.bounds) pen.Rectangle(this.bounds)
} }
if this.texture != nil { if this.texture != nil {
@ -431,7 +431,7 @@ func (this *box) determineFillTransparency () {
_, _, _, a := this.color.RGBA() _, _, _, a := this.color.RGBA()
this.fillTransparent = this.fillTransparent =
a != 0xFFFF && a != 0xFFFF &&
!(this.texture != nil && !this.texture.transparent) !(this.texture != nil && this.texture.Opaque())
} }
func (this *box) propagate (callback func (anyBox) bool) bool { func (this *box) propagate (callback func (anyBox) bool) bool {

View File

@ -4,22 +4,23 @@ import "image"
import "image/color" import "image/color"
import "github.com/jezek/xgbutil" import "github.com/jezek/xgbutil"
import "github.com/jezek/xgb/xproto" import "github.com/jezek/xgb/xproto"
import "git.tebibyte.media/tomo/ggfx"
import "github.com/jezek/xgbutil/xgraphics" import "github.com/jezek/xgbutil/xgraphics"
import "git.tebibyte.media/tomo/tomo/canvas" import "git.tebibyte.media/tomo/tomo/canvas"
// Canvas satisfies the canvas.Canvas interface. It draws to an xgraphics.Image. // Canvas satisfies the canvas.Canvas interface. It draws to an xgraphics.Image.
// It must be closed after use.
type Canvas struct { type Canvas struct {
*xgraphics.Image *xgraphics.Image
} }
// New creates a new canvas from a bounding rectangle. // NewCanvas creates a new canvas from a bounding rectangle.
func New (x *xgbutil.XUtil, bounds image.Rectangle) *Canvas { func NewCanvas (x *xgbutil.XUtil, bounds image.Rectangle) *Canvas {
return NewFrom(xgraphics.New(x, bounds)) return NewCanvasFrom(xgraphics.New(x, bounds))
} }
// NewFrom creates a new canvas from an existing xgraphics.Image. // NewCanvasFrom creates a new canvas from an existing xgraphics.Image. Note
func NewFrom (image *xgraphics.Image) *Canvas { // that calling Close() on the resulting canvas will destroy this image.
func NewCanvasFrom (image *xgraphics.Image) *Canvas {
if image == nil { return nil } if image == nil { return nil }
return &Canvas { image } return &Canvas { image }
} }
@ -28,12 +29,6 @@ func NewFrom (image *xgraphics.Image) *Canvas {
func (this *Canvas) Pen () canvas.Pen { func (this *Canvas) Pen () canvas.Pen {
return &pen { return &pen {
image: this.Image, 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) 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 () { func (this *Canvas) assert () {
if this == nil { panic("nil canvas") } if this == nil { panic("nil canvas") }
} }
@ -64,35 +65,34 @@ func (this *Canvas) assert () {
type pen struct { type pen struct {
image *xgraphics.Image image *xgraphics.Image
gfx ggfx.Image[uint8]
closed bool closed bool
endCap canvas.Cap endCap canvas.Cap
joint canvas.Joint joint canvas.Joint
weight int weight int
align canvas.StrokeAlign align canvas.StrokeAlign
stroke [4]uint8 stroke xgraphics.BGRA
fill [4]uint8 fill xgraphics.BGRA
} }
func (this *pen) Rectangle (bounds image.Rectangle) { func (this *pen) Rectangle (bounds image.Rectangle) {
if this.weight == 0 { if this.weight == 0 {
this.gfx.FillRectangle(this.fill[:], bounds) this.gfx.fillRectangle(bounds)
} else { } else {
this.gfx.StrokeRectangle(this.stroke[:], this.weight, bounds) this.gfx.strokeRectangle(bounds)
} }
} }
func (this *pen) Path (points ...image.Point) { func (this *pen) Path (points ...image.Point) {
if this.weight == 0 { if this.weight == 0 {
this.gfx.FillPolygon(this.fill[:], points...) this.fillPolygon(points...)
} else if this.closed { } else if this.closed {
this.gfx.StrokePolygon(this.stroke[:], this.weight, points...) this.strokePolygon(points...)
} else { } else {
this.gfx.PolyLine(this.stroke[:], this.weight, points...) this.polyLine(points...)
} }
} }
func (this *pen) Closed (closed bool) { this.closed = closed } func (this *pen) Closed (closed bool) { this.closed = closed }
func (this *pen) Cap (endCap canvas.Cap) { this.endCap = endCap } func (this *pen) Cap (endCap canvas.Cap) { this.endCap = endCap }
func (this *pen) Joint (joint canvas.Joint) { this.joint = joint } 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) StrokeAlign (align canvas.StrokeAlign) { this.align = align }
func (this *pen) Stroke (stroke color.Color) { this.stroke = convertColor(stroke) } 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() r, g, b, a := c.RGBA()
return [4]uint8 { return xgraphics.BGRA {
uint8(b >> 8), B: uint8(b >> 8),
uint8(g >> 8), G: uint8(g >> 8),
uint8(r >> 8), R: uint8(r >> 8),
uint8(a >> 8), A: uint8(a >> 8),
} }
} }

5
canvas/draw.go Normal file
View File

@ -0,0 +1,5 @@
package xcanvas
func (this *pen) fillRectangle (bounds image.Rectangle) {
}

60
canvas/texture.go Normal file
View File

@ -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
}

View File

@ -143,7 +143,7 @@ func (this *containerBox) Draw (can canvas.Canvas) {
if this.fillTransparent && this.parent != nil { if this.fillTransparent && this.parent != nil {
this.parent.drawBackgroundPart(can.Clip(tile)) this.parent.drawBackgroundPart(can.Clip(tile))
} }
if this.texture == nil || this.texture.transparent { if this.texture == nil || !this.texture.Opaque() {
pen.Rectangle(tile) pen.Rectangle(tile)
} }
if this.texture != nil { if this.texture != nil {
@ -162,7 +162,7 @@ func (this *containerBox) drawBackgroundPart (can canvas.Canvas) {
pen := can.Pen() pen := can.Pen()
pen.Fill(this.color) pen.Fill(this.color)
if this.texture == nil || this.texture.transparent { if this.texture == nil || !this.texture.Opaque() {
pen.Rectangle(can.Bounds()) pen.Rectangle(can.Bounds())
} }
if this.texture != nil { if this.texture != nil {

View File

@ -2,49 +2,22 @@ package x
import "image" import "image"
import "git.tebibyte.media/tomo/tomo" import "git.tebibyte.media/tomo/tomo"
import "git.tebibyte.media/tomo/x/canvas"
type texture struct { type texture struct {
pix []uint8 *xcanvas.Texture
stride int
rect image.Rectangle
transparent bool
} }
func (backend *Backend) NewTexture (source image.Image) tomo.Texture { func (backend Backend) NewTexture (source image.Image) tomo.Texture {
bounds := source.Bounds() return texture {
texture := &texture { Texture: xcanvas.NewTextureFrom(source),
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
} }
func (this *texture) Clip (bounds image.Rectangle) tomo.Texture { func (this texture) Clip (bounds image.Rectangle) tomo.Texture {
clipped := *this return texture {
clipped.rect = bounds Texture: this.Texture.Clip(bounds),
return &clipped }
}
func (this *texture) Close () error {
return nil
} }
func assertTexture (unknown tomo.Texture) *texture { func assertTexture (unknown tomo.Texture) *texture {

View File

@ -332,7 +332,7 @@ func (window *window) reallocateCanvas () {
if window.xCanvas != nil { if window.xCanvas != nil {
window.xCanvas.Destroy() window.xCanvas.Destroy()
} }
window.xCanvas = xcanvas.NewFrom(xgraphics.New ( window.xCanvas = xcanvas.NewCanvasFrom(xgraphics.New (
window.backend.x, window.backend.x,
image.Rect ( image.Rect (
0, 0, 0, 0,