129 lines
3.1 KiB
Go
129 lines
3.1 KiB
Go
|
package x
|
||
|
|
||
|
import "image"
|
||
|
import "image/draw"
|
||
|
import "golang.org/x/image/math/fixed"
|
||
|
|
||
|
import "git.tebibyte.media/sashakoshka/stone"
|
||
|
|
||
|
func (backend *Backend) Draw () {
|
||
|
backend.drawLock.Lock()
|
||
|
defer backend.drawLock.Unlock()
|
||
|
|
||
|
boundsChanged :=
|
||
|
backend.memory.windowWidth != backend.metrics.windowWidth ||
|
||
|
backend.memory.windowHeight != backend.metrics.windowHeight
|
||
|
backend.memory.windowWidth = backend.metrics.windowWidth
|
||
|
backend.memory.windowHeight = backend.metrics.windowHeight
|
||
|
|
||
|
if boundsChanged {
|
||
|
backend.reallocateCanvas()
|
||
|
backend.drawCells(true)
|
||
|
backend.canvas.XDraw()
|
||
|
backend.canvas.XPaint(backend.window.Id)
|
||
|
} else {
|
||
|
backend.updateWindowAreas(backend.drawCells(false)...)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (backend *Backend) updateWindowAreas (areas ...image.Rectangle) {
|
||
|
backend.canvas.XPaintRects(backend.window.Id, areas...)
|
||
|
}
|
||
|
|
||
|
func (backend *Backend) drawRune (x, y int, character rune) {
|
||
|
// 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.
|
||
|
|
||
|
fillRectangle (
|
||
|
&image.Uniform {
|
||
|
C: backend.config.Color(stone.ColorApplication),
|
||
|
},
|
||
|
backend.canvas,
|
||
|
backend.boundsOfCell(x, y))
|
||
|
|
||
|
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)
|
||
|
|
||
|
// strokeRectangle (
|
||
|
// &image.Uniform {
|
||
|
// C: backend.config.Color(stone.ColorForeground),
|
||
|
// },
|
||
|
// backend.canvas,
|
||
|
// backend.boundsOfCell(x, y))
|
||
|
|
||
|
draw.DrawMask (
|
||
|
backend.canvas,
|
||
|
destinationRectangle,
|
||
|
&image.Uniform {
|
||
|
C: backend.config.Color(stone.ColorForeground),
|
||
|
},
|
||
|
image.Point { },
|
||
|
mask,
|
||
|
maskPoint,
|
||
|
draw.Over)
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
}}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
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))
|
||
|
}
|
||
|
}
|