stone/backends/x/draw.go

161 lines
3.6 KiB
Go
Raw Normal View History

2022-11-13 20:44:19 -07:00
package x
import "image"
import "image/draw"
import "golang.org/x/image/math/fixed"
2022-11-15 22:29:23 -07:00
import "github.com/jezek/xgbutil/xgraphics"
2022-11-13 20:44:19 -07:00
import "git.tebibyte.media/sashakoshka/stone"
func (backend *Backend) Draw () {
2022-11-15 22:29:23 -07:00
backend.lock.Lock()
defer backend.lock.Unlock()
2022-11-13 20:44:19 -07:00
if backend.windowBoundsClean {
backend.canvas.XPaintRects (
backend.window.Id,
backend.drawCells(false)...)
} else {
2022-11-13 20:44:19 -07:00
backend.reallocateCanvas()
backend.drawCells(true)
backend.canvas.XDraw()
backend.canvas.XPaint(backend.window.Id)
backend.windowBoundsClean = true
2022-11-13 20:44:19 -07:00
}
}
2022-11-15 22:29:23 -07:00
func (backend *Backend) reallocateCanvas () {
if backend.canvas != nil {
backend.canvas.Destroy()
}
backend.canvas = xgraphics.New (
backend.connection,
image.Rect (
0, 0,
backend.metrics.windowWidth,
backend.metrics.windowHeight))
backend.canvas.For (func (x, y int) xgraphics.BGRA {
return backend.colors[stone.ColorBackground]
})
backend.canvas.XSurfaceSet(backend.window.Id)
}
func (backend *Backend) drawCells (forceRedraw bool) (areas []image.Rectangle) {
width, height := backend.application.Size()
for y := 0; y < height; y ++ {
for x := 0; x < width; x ++ {
if !forceRedraw && backend.application.Clean(x, y) { continue }
backend.application.MarkClean(x, y)
cell := backend.application.Cell(x, y)
content := cell.Rune()
if forceRedraw && content < 32 { continue }
areas = append(areas, backend.boundsOfCell(x, y))
backend.drawRune(x, y, content, cell.Color(), !forceRedraw)
}}
if backend.drawBufferBounds && forceRedraw {
strokeRectangle (
&image.Uniform {
C: backend.config.Color(stone.ColorForeground),
},
backend.canvas,
image.Rectangle {
Min: backend.originOfCell(0, 0),
Max: backend.originOfCell(width, height),
})
}
return
2022-11-13 20:44:19 -07:00
}
2022-11-15 22:29:23 -07:00
func (backend *Backend) drawRune (
x, y int,
character rune,
runeColor stone.Color,
drawBackground bool,
) {
2022-11-13 20:44:19 -07:00
// TODO: cache these draws as non-transparent buffers with the
// application background color as the background. that way, we won't
// need to redraw the characters *or* composite them.
2022-11-15 22:29:23 -07:00
if drawBackground {
fillRectangle (
&image.Uniform {
C: backend.config.Color(stone.ColorBackground),
},
backend.canvas,
backend.boundsOfCell(x, y))
}
2022-11-13 20:44:19 -07:00
if character < 32 { return }
origin := backend.originOfCell(x, y + 1)
destinationRectangle, mask, maskPoint, _, _ := backend.font.face.Glyph (
fixed.Point26_6 {
X: fixed.I(origin.X),
Y: fixed.I(origin.Y - backend.metrics.descent),
},
character)
if backend.drawCellBounds {
strokeRectangle (
&image.Uniform {
C: backend.config.Color(stone.ColorForeground),
},
backend.canvas,
backend.boundsOfCell(x, y))
}
2022-11-13 20:44:19 -07:00
draw.DrawMask (
backend.canvas,
destinationRectangle,
&image.Uniform {
2022-11-15 09:16:29 -07:00
C: backend.config.Color(runeColor),
2022-11-13 20:44:19 -07:00
},
image.Point { },
mask,
maskPoint,
draw.Over)
}
func fillRectangle (
source image.Image,
destination draw.Image,
bounds image.Rectangle,
) {
for y := bounds.Min.Y; y < bounds.Max.Y; y ++ {
for x := bounds.Min.X; x < bounds.Max.X; x ++ {
destination.Set(x, y, source.At(x, y))
}}
}
func strokeRectangle (
source image.Image,
destination draw.Image,
bounds image.Rectangle,
) {
x := 0
y := bounds.Min.Y
for x = bounds.Min.X; x < bounds.Max.X; x ++ {
destination.Set(x, y, source.At(x, y))
}
y = bounds.Max.Y - 1
for x = bounds.Min.X; x < bounds.Max.X; x ++ {
destination.Set(x, y, source.At(x, y))
}
x = bounds.Min.X
for y = bounds.Min.Y; y < bounds.Max.Y; y ++ {
destination.Set(x, y, source.At(x, y))
}
x = bounds.Max.X - 1
for y = bounds.Min.Y; y < bounds.Max.Y; y ++ {
destination.Set(x, y, source.At(x, y))
}
}