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, runeColor stone.Color) { // 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.ColorBackground), }, 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) 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 (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()) }} 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 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)) } }