2022-11-13 20:44:19 -07:00
|
|
|
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)
|
2022-11-14 21:32:05 -07:00
|
|
|
|
|
|
|
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 {
|
|
|
|
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)
|
|
|
|
}}
|
2022-11-14 21:32:05 -07:00
|
|
|
|
|
|
|
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),
|
|
|
|
})
|
|
|
|
}
|
2022-11-13 20:44:19 -07:00
|
|
|
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))
|
|
|
|
}
|
|
|
|
}
|