From c18b702b61d1dbd58d10584f6f784b3716a3900c Mon Sep 17 00:00:00 2001 From: Liam Galvin Date: Sat, 31 Jul 2021 17:35:26 +0100 Subject: [PATCH] Fix issues with transparency (#301) * Fix issues with transparency * Dispose of temporary alpha image --- internal/app/darktile/cmd/root.go | 1 + internal/app/darktile/config/default.go | 2 +- internal/app/darktile/gui/draw.go | 50 +++++++++++-------- internal/app/darktile/gui/gui.go | 2 + internal/app/darktile/gui/options.go | 7 +++ internal/app/darktile/termutil/theme.go | 19 ++++--- .../app/darktile/termutil/theme_factory.go | 8 +-- 7 files changed, 49 insertions(+), 40 deletions(-) diff --git a/internal/app/darktile/cmd/root.go b/internal/app/darktile/cmd/root.go index 25a7e937..0df4279f 100644 --- a/internal/app/darktile/cmd/root.go +++ b/internal/app/darktile/cmd/root.go @@ -90,6 +90,7 @@ var rootCmd = &cobra.Command{ gui.WithFontDPI(conf.Font.DPI), gui.WithFontSize(conf.Font.Size), gui.WithFontFamily(conf.Font.Family), + gui.WithOpacity(conf.Opacity), } if screenshotAfterMS > 0 { diff --git a/internal/app/darktile/config/default.go b/internal/app/darktile/config/default.go index 94cb8b17..e225c5b8 100644 --- a/internal/app/darktile/config/default.go +++ b/internal/app/darktile/config/default.go @@ -73,7 +73,7 @@ func LoadThemeFromPath(conf *Config, path string) (*termutil.Theme, error) { func loadThemeFromConf(conf *Config, themeConf *Theme) (*termutil.Theme, error) { - factory := termutil.NewThemeFactory().WithOpacity(conf.Opacity) + factory := termutil.NewThemeFactory() colours := map[termutil.Colour]string{ termutil.ColourBlack: themeConf.Black, diff --git a/internal/app/darktile/gui/draw.go b/internal/app/darktile/gui/draw.go index 8641fac7..1d93780d 100644 --- a/internal/app/darktile/gui/draw.go +++ b/internal/app/darktile/gui/draw.go @@ -14,6 +14,8 @@ import ( // Draw renders the terminal GUI to the ebtien window. Required to implement the ebiten interface. func (g *GUI) Draw(screen *ebiten.Image) { + tmp := ebiten.NewImage(g.size.X, g.size.Y) + cellSize := g.fontManager.CharSize() dotDepth := g.fontManager.DotDepth() @@ -36,10 +38,10 @@ func (g *GUI) Draw(screen *ebiten.Image) { extraW := float64(g.size.X) - endX extraH := float64(g.size.Y) - endY if extraW > 0 { - ebitenutil.DrawRect(screen, endX, 0, extraW, endY, defBg) + ebitenutil.DrawRect(tmp, endX, 0, extraW, endY, defBg) } if extraH > 0 { - ebitenutil.DrawRect(screen, 0, endY, float64(g.size.X), extraH, defBg) + ebitenutil.DrawRect(tmp, 0, endY, float64(g.size.X), extraH, defBg) } var inHighlight bool @@ -52,7 +54,7 @@ func (g *GUI) Draw(screen *ebiten.Image) { for y := int(buffer.ViewHeight() - 1); y >= 0; y-- { py := cellSize.Y * y - ebitenutil.DrawRect(screen, 0, float64(py), float64(g.size.X), float64(cellSize.Y), defBg) + ebitenutil.DrawRect(tmp, 0, float64(py), float64(g.size.X), float64(cellSize.Y), defBg) inHighlight = false for x := uint16(0); x < buffer.ViewWidth(); x++ { cell := buffer.GetCell(x, uint16(y)) @@ -74,7 +76,7 @@ func (g *GUI) Draw(screen *ebiten.Image) { colour = defBg } - ebitenutil.DrawRect(screen, float64(px), float64(py), float64(cellSize.X), float64(cellSize.Y), colour) + ebitenutil.DrawRect(tmp, float64(px), float64(py), float64(cellSize.X), float64(cellSize.Y), colour) if buffer.IsHighlighted(termutil.Position{ Line: uint64(y), @@ -106,7 +108,7 @@ func (g *GUI) Draw(screen *ebiten.Image) { } if isCursor && !ebiten.IsFocused() { - ebitenutil.DrawRect(screen, float64(px)+1, float64(py)+1, float64(cellSize.X)-2, float64(cellSize.Y)-2, g.terminal.Theme().DefaultBackground()) + ebitenutil.DrawRect(tmp, float64(px)+1, float64(py)+1, float64(cellSize.X)-2, float64(cellSize.Y)-2, g.terminal.Theme().DefaultBackground()) } } for x := uint16(0); x < buffer.ViewWidth(); x++ { @@ -139,13 +141,13 @@ func (g *GUI) Draw(screen *ebiten.Image) { if cell.Underline() { uly := float64(py + (dotDepth+cellSize.Y)/2) - ebitenutil.DrawLine(screen, float64(px), uly, float64(px+cellSize.X), uly, colour) + ebitenutil.DrawLine(tmp, float64(px), uly, float64(px+cellSize.X), uly, colour) } - text.Draw(screen, string(cell.Rune().Rune), useFace, px, py+dotDepth, colour) + text.Draw(tmp, string(cell.Rune().Rune), useFace, px, py+dotDepth, colour) if cell.Strikethrough() { - ebitenutil.DrawLine(screen, float64(px), float64(py+(cellSize.Y/2)), float64(px+cellSize.X), float64(py+(cellSize.Y/2)), colour) + ebitenutil.DrawLine(tmp, float64(px), float64(py+(cellSize.Y/2)), float64(px+cellSize.X), float64(py+(cellSize.Y/2)), colour) } } @@ -157,7 +159,7 @@ func (g *GUI) Draw(screen *ebiten.Image) { op := &ebiten.DrawImageOptions{} op.GeoM.Translate(sx, sy) - screen.DrawImage( + tmp.DrawImage( ebiten.NewImageFromImage(sixel.Sixel.Image), op, ) @@ -237,19 +239,19 @@ func (g *GUI) Draw(screen *ebiten.Image) { } // draw opaque box below and above highlighted line(s) - ebitenutil.DrawRect(screen, 0, float64(highlightMin.Line*uint64(cellSize.Y)), float64(cellSize.X*int(highlightMin.Col)), float64(cellSize.Y), color.RGBA{A: 0x80}) - ebitenutil.DrawRect(screen, float64((cellSize.X)*int(highlightMax.Col+1)), float64(highlightMax.Line*uint64(cellSize.Y)), float64(g.size.X), float64(cellSize.Y), color.RGBA{A: 0x80}) - ebitenutil.DrawRect(screen, 0, 0, float64(g.size.X), float64(highlightMin.Line*uint64(cellSize.Y)), color.RGBA{A: 0x80}) + ebitenutil.DrawRect(tmp, 0, float64(highlightMin.Line*uint64(cellSize.Y)), float64(cellSize.X*int(highlightMin.Col)), float64(cellSize.Y), color.RGBA{A: 0x80}) + ebitenutil.DrawRect(tmp, float64((cellSize.X)*int(highlightMax.Col+1)), float64(highlightMax.Line*uint64(cellSize.Y)), float64(g.size.X), float64(cellSize.Y), color.RGBA{A: 0x80}) + ebitenutil.DrawRect(tmp, 0, 0, float64(g.size.X), float64(highlightMin.Line*uint64(cellSize.Y)), color.RGBA{A: 0x80}) afterLineY := float64((1 + highlightMax.Line) * uint64(cellSize.Y)) - ebitenutil.DrawRect(screen, 0, afterLineY, float64(g.size.X), float64(g.size.Y)-afterLineY, color.RGBA{A: 0x80}) + ebitenutil.DrawRect(tmp, 0, afterLineY, float64(g.size.X), float64(g.size.Y)-afterLineY, color.RGBA{A: 0x80}) // annotation border - ebitenutil.DrawRect(screen, float64(annotationX)-padding, annotationY-padding, float64(annotationWidth)+(padding*2), annotationHeight+(padding*2), g.terminal.Theme().SelectionBackground()) + ebitenutil.DrawRect(tmp, float64(annotationX)-padding, annotationY-padding, float64(annotationWidth)+(padding*2), annotationHeight+(padding*2), g.terminal.Theme().SelectionBackground()) // annotation background - ebitenutil.DrawRect(screen, 1+float64(annotationX)-padding, 1+annotationY-padding, float64(annotationWidth)+(padding*2)-2, annotationHeight+(padding*2)-2, g.terminal.Theme().DefaultBackground()) + ebitenutil.DrawRect(tmp, 1+float64(annotationX)-padding, 1+annotationY-padding, float64(annotationWidth)+(padding*2)-2, annotationHeight+(padding*2)-2, g.terminal.Theme().DefaultBackground()) // vertical line - ebitenutil.DrawLine(screen, lineX, float64(lineY), lineX, lineY+lineHeight, g.terminal.Theme().SelectionBackground()) + ebitenutil.DrawLine(tmp, lineX, float64(lineY), lineX, lineY+lineHeight, g.terminal.Theme().SelectionBackground()) var tY int var tX int @@ -259,7 +261,7 @@ func (g *GUI) Draw(screen *ebiten.Image) { op := &ebiten.DrawImageOptions{} op.GeoM.Translate(float64(annotationX), annotationY) - screen.DrawImage( + tmp.DrawImage( ebiten.NewImageFromImage(annotation.Image), op, ) @@ -271,7 +273,7 @@ func (g *GUI) Draw(screen *ebiten.Image) { tX = 0 continue } - text.Draw(screen, string(r), regularFace, annotationX+tX, int(annotationY)+dotDepth+tY, g.terminal.Theme().DefaultForeground()) + text.Draw(tmp, string(r), regularFace, annotationX+tX, int(annotationY)+dotDepth+tY, g.terminal.Theme().DefaultForeground()) tX += cellSize.X } @@ -298,11 +300,11 @@ func (g *GUI) Draw(screen *ebiten.Image) { boxWidth = endX / 8 } - ebitenutil.DrawRect(screen, float64(msgX-1), msgY-1, boxWidth+2, boxHeight+2, msg.Foreground) - ebitenutil.DrawRect(screen, float64(msgX), msgY, boxWidth, boxHeight, msg.Background) + ebitenutil.DrawRect(tmp, float64(msgX-1), msgY-1, boxWidth+2, boxHeight+2, msg.Foreground) + ebitenutil.DrawRect(tmp, float64(msgX), msgY, boxWidth, boxHeight, msg.Background) for y, line := range lines { for x, r := range line { - text.Draw(screen, string(r), regularFace, msgX+pad+(x*cellSize.X), pad+(y*cellSize.Y)+int(msgY)+dotDepth, msg.Foreground) + text.Draw(tmp, string(r), regularFace, msgX+pad+(x*cellSize.X), pad+(y*cellSize.Y)+int(msgY)+dotDepth, msg.Foreground) } } msgEndY = msgEndY - float64(pad*4) - float64(len(lines)*g.CellSize().Y) @@ -310,7 +312,11 @@ func (g *GUI) Draw(screen *ebiten.Image) { } if g.screenshotRequested { - g.takeScreenshot(screen) + g.takeScreenshot(tmp) } + opt := &ebiten.DrawImageOptions{} + opt.ColorM.Scale(1, 1, 1, g.opacity) + screen.DrawImage(tmp, opt) + tmp.Dispose() } diff --git a/internal/app/darktile/gui/gui.go b/internal/app/darktile/gui/gui.go index 37f77931..63b436b2 100644 --- a/internal/app/darktile/gui/gui.go +++ b/internal/app/darktile/gui/gui.go @@ -39,6 +39,7 @@ type GUI struct { screenshotFilename string startupFuncs []func(g *GUI) keyState *keyState + opacity float64 } type PopupMessage struct { @@ -88,6 +89,7 @@ func (g *GUI) Run() error { }() ebiten.SetScreenTransparent(true) + ebiten.SetScreenClearedEveryFrame(true) ebiten.SetWindowResizable(true) ebiten.SetRunnableOnUnfocused(true) ebiten.SetFPSMode(ebiten.FPSModeVsyncOffMinimum) diff --git a/internal/app/darktile/gui/options.go b/internal/app/darktile/gui/options.go index e6597967..7a0fe50d 100644 --- a/internal/app/darktile/gui/options.go +++ b/internal/app/darktile/gui/options.go @@ -8,6 +8,13 @@ func WithFontFamily(family string) func(g *GUI) error { } } +func WithOpacity(opacity float64) func(g *GUI) error { + return func(g *GUI) error { + g.opacity = opacity + return nil + } +} + func WithFontSize(size float64) func(g *GUI) error { return func(g *GUI) error { g.fontManager.SetSize(size) diff --git a/internal/app/darktile/termutil/theme.go b/internal/app/darktile/termutil/theme.go index 28d6df3e..b2f515a2 100644 --- a/internal/app/darktile/termutil/theme.go +++ b/internal/app/darktile/termutil/theme.go @@ -35,7 +35,6 @@ const ( ) type Theme struct { - alpha uint8 colourMap map[Colour]color.Color } @@ -87,7 +86,7 @@ func (t *Theme) ColourFrom4Bit(code uint8) color.Color { func (t *Theme) DefaultBackground() color.Color { c, ok := t.colourMap[ColourBackground] if !ok { - return color.RGBA{0, 0, 0, t.alpha} + return color.RGBA{0, 0, 0, 0xff} } return c } @@ -95,7 +94,7 @@ func (t *Theme) DefaultBackground() color.Color { func (t *Theme) DefaultForeground() color.Color { c, ok := t.colourMap[ColourForeground] if !ok { - return color.RGBA{255, 255, 255, t.alpha} + return color.RGBA{255, 255, 255, 0xff} } return c } @@ -103,7 +102,7 @@ func (t *Theme) DefaultForeground() color.Color { func (t *Theme) SelectionBackground() color.Color { c, ok := t.colourMap[ColourSelectionBackground] if !ok { - return color.RGBA{0, 0, 0, t.alpha} + return color.RGBA{0, 0, 0, 0xff} } return c } @@ -111,7 +110,7 @@ func (t *Theme) SelectionBackground() color.Color { func (t *Theme) SelectionForeground() color.Color { c, ok := t.colourMap[ColourSelectionForeground] if !ok { - return color.RGBA{255, 255, 255, t.alpha} + return color.RGBA{255, 255, 255, 0xff} } return c } @@ -119,7 +118,7 @@ func (t *Theme) SelectionForeground() color.Color { func (t *Theme) CursorBackground() color.Color { c, ok := t.colourMap[ColourCursorBackground] if !ok { - return color.RGBA{255, 255, 255, t.alpha} + return color.RGBA{255, 255, 255, 0xff} } return c } @@ -127,7 +126,7 @@ func (t *Theme) CursorBackground() color.Color { func (t *Theme) CursorForeground() color.Color { c, ok := t.colourMap[ColourCursorForeground] if !ok { - return color.RGBA{0, 0, 0, t.alpha} + return color.RGBA{0, 0, 0, 0xff} } return c } @@ -149,12 +148,12 @@ func (t *Theme) ColourFrom8Bit(n string) (color.Color, error) { R: byte(c), G: byte(c), B: byte(c), - A: t.alpha, + A: 0xff, }, nil } var colour color.RGBA - colour.A = t.alpha + colour.A = 0xff indexR := ((index - 16) / 36) if indexR > 0 { colour.R = uint8(55 + indexR*40) @@ -188,7 +187,7 @@ func (t *Theme) ColourFrom24Bit(r, g, b string) (color.Color, error) { R: byte(ri), G: byte(gi), B: byte(bi), - A: t.alpha, + A: 0xff, }, nil } diff --git a/internal/app/darktile/termutil/theme_factory.go b/internal/app/darktile/termutil/theme_factory.go index a667bc54..5f3bb57f 100644 --- a/internal/app/darktile/termutil/theme_factory.go +++ b/internal/app/darktile/termutil/theme_factory.go @@ -10,7 +10,6 @@ type ThemeFactory struct { func NewThemeFactory() *ThemeFactory { return &ThemeFactory{ theme: &Theme{ - alpha: 0xff, colourMap: map[Colour]color.Color{}, }, colourMap: make(map[Colour]color.Color), @@ -24,17 +23,12 @@ func (t *ThemeFactory) Build() *Theme { R: uint8(r / 0xff), G: uint8(g / 0xff), B: uint8(b / 0xff), - A: t.theme.alpha, + A: 0xff, } } return t.theme } -func (t *ThemeFactory) WithOpacity(opacity float64) *ThemeFactory { - t.theme.alpha = uint8(0xff * opacity) - return t -} - func (t *ThemeFactory) WithColour(key Colour, colour color.Color) *ThemeFactory { t.colourMap[key] = colour return t