171 lines
3.8 KiB
Go
171 lines
3.8 KiB
Go
package x
|
|
|
|
import "image"
|
|
import "image/draw"
|
|
import "golang.org/x/image/math/fixed"
|
|
import "github.com/jezek/xgbutil/xgraphics"
|
|
|
|
import "git.tebibyte.media/sashakoshka/stone"
|
|
|
|
func (backend *Backend) Draw () {
|
|
backend.lock.Lock()
|
|
defer backend.lock.Unlock()
|
|
|
|
if backend.windowBoundsClean {
|
|
backend.canvas.XPaintRects (
|
|
backend.window.Id,
|
|
backend.drawCells(false)...)
|
|
} else {
|
|
backend.reallocateCanvas()
|
|
backend.drawCells(true)
|
|
backend.canvas.XDraw()
|
|
backend.canvas.XPaint(backend.window.Id)
|
|
backend.windowBoundsClean = true
|
|
}
|
|
}
|
|
|
|
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 }
|
|
|
|
cell := backend.application.GetForRendering(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
|
|
}
|
|
|
|
func (backend *Backend) drawRune (
|
|
x, y int,
|
|
character rune,
|
|
runeColor stone.Color,
|
|
drawBackground bool,
|
|
) {
|
|
// 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.
|
|
|
|
if drawBackground {
|
|
fillRectangle (
|
|
&image.Uniform {
|
|
C: backend.config.Color(stone.ColorBackground),
|
|
},
|
|
backend.canvas,
|
|
backend.boundsOfCell(x, y))
|
|
}
|
|
|
|
if character < 32 { return }
|
|
|
|
origin := backend.originOfCell(x, y + 1)
|
|
destinationRectangle, mask, maskPoint, _, ok := backend.font.face.Glyph (
|
|
fixed.Point26_6 {
|
|
X: fixed.I(origin.X),
|
|
Y: fixed.I(origin.Y - backend.metrics.descent),
|
|
},
|
|
character)
|
|
|
|
if !ok {
|
|
println("warning")
|
|
strokeRectangle (
|
|
&image.Uniform {
|
|
C: backend.config.Color(stone.ColorForeground),
|
|
},
|
|
backend.canvas,
|
|
backend.boundsOfCell(x, y))
|
|
return
|
|
}
|
|
|
|
if backend.drawCellBounds {
|
|
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(runeColor),
|
|
},
|
|
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))
|
|
}
|
|
}
|