mirror of
https://github.com/Luzifer/streamdeck.git
synced 2024-12-20 09:41:19 +00:00
Add button caption rendering
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
90df45ac3d
commit
40ef541372
4 changed files with 83 additions and 7 deletions
|
@ -1,9 +1,16 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
AutoReload bool `yaml:"auto_reload"`
|
AutoReload bool `yaml:"auto_reload"`
|
||||||
|
CaptionBorder int `yaml:"caption_border"`
|
||||||
|
CaptionColor [4]int `yaml:"caption_color"`
|
||||||
|
CaptionFont string `yaml:"caption_font"`
|
||||||
|
CaptionFontSize float64 `yaml:"caption_font_size"`
|
||||||
|
CaptionPosition captionPosition `yaml:"caption_position"`
|
||||||
DefaultBrightness int `yaml:"default_brightness"`
|
DefaultBrightness int `yaml:"default_brightness"`
|
||||||
DefaultPage string `yaml:"default_page"`
|
DefaultPage string `yaml:"default_page"`
|
||||||
DisplayOffTime time.Duration `yaml:"display_off_time"`
|
DisplayOffTime time.Duration `yaml:"display_off_time"`
|
||||||
|
@ -31,3 +38,10 @@ func newConfig() config {
|
||||||
AutoReload: true,
|
AutoReload: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type captionPosition string
|
||||||
|
|
||||||
|
const (
|
||||||
|
captionPositionBottom = "bottom"
|
||||||
|
captionPositionTop = "top"
|
||||||
|
)
|
||||||
|
|
|
@ -131,6 +131,12 @@ func (d displayElementExec) Display(ctx context.Context, idx int, attributes map
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if caption, ok := attributes["caption"].(string); ok && strings.TrimSpace(caption) != "" {
|
||||||
|
if err = imgRenderer.DrawCaptionText(strings.TrimSpace(caption)); err != nil {
|
||||||
|
return errors.Wrap(err, "Unable to render caption")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !d.running && d.NeedsLoop(attributes) {
|
if !d.running && d.NeedsLoop(attributes) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -112,6 +113,12 @@ func (d displayElementPulseVolume) Display(ctx context.Context, idx int, attribu
|
||||||
return errors.Wrap(err, "Unable to draw text")
|
return errors.Wrap(err, "Unable to draw text")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if caption, ok := attributes["caption"].(string); ok && strings.TrimSpace(caption) != "" {
|
||||||
|
if err = img.DrawCaptionText(strings.TrimSpace(caption)); err != nil {
|
||||||
|
return errors.Wrap(err, "Unable to render caption")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err = ctx.Err(); err != nil {
|
if err = ctx.Err(); err != nil {
|
||||||
// Page context was cancelled, do not draw
|
// Page context was cancelled, do not draw
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -15,6 +15,14 @@ import (
|
||||||
"golang.org/x/image/math/fixed"
|
"golang.org/x/image/math/fixed"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type textDrawAnchor uint
|
||||||
|
|
||||||
|
const (
|
||||||
|
textDrawAnchorCenter textDrawAnchor = iota
|
||||||
|
textDrawAnchorBottom
|
||||||
|
textDrawAnchorTop
|
||||||
|
)
|
||||||
|
|
||||||
type textOnImageRenderer struct {
|
type textOnImageRenderer struct {
|
||||||
img draw.Image
|
img draw.Image
|
||||||
}
|
}
|
||||||
|
@ -46,7 +54,7 @@ func (t *textOnImageRenderer) DrawBackgroundFromFile(filename string) error {
|
||||||
|
|
||||||
func (t *textOnImageRenderer) DrawBigText(text string, fontSizeHint float64, border int, textColor color.Color) error {
|
func (t *textOnImageRenderer) DrawBigText(text string, fontSizeHint float64, border int, textColor color.Color) error {
|
||||||
// Render text
|
// Render text
|
||||||
f, err := t.loadFont()
|
f, err := t.loadFont(userConfig.RenderFont)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Unable to load font")
|
return errors.Wrap(err, "Unable to load font")
|
||||||
}
|
}
|
||||||
|
@ -58,12 +66,44 @@ func (t *textOnImageRenderer) DrawBigText(text string, fontSizeHint float64, bor
|
||||||
c.SetFont(f)
|
c.SetFont(f)
|
||||||
c.SetHinting(font.HintingNone)
|
c.SetHinting(font.HintingNone)
|
||||||
|
|
||||||
return t.drawText(c, text, textColor, fontSizeHint, border)
|
return t.drawText(c, text, textColor, fontSizeHint, border, textDrawAnchorCenter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *textOnImageRenderer) DrawCaptionText(text string) error {
|
||||||
|
// Render text
|
||||||
|
f, err := t.loadFont(userConfig.CaptionFont)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Unable to load font")
|
||||||
|
}
|
||||||
|
|
||||||
|
var textColor color.Color = color.RGBA{0xff, 0xff, 0xff, 0xff}
|
||||||
|
if userConfig.CaptionColor[3] != 0x0 {
|
||||||
|
textColor = color.RGBA{
|
||||||
|
uint8(userConfig.CaptionColor[0]),
|
||||||
|
uint8(userConfig.CaptionColor[1]),
|
||||||
|
uint8(userConfig.CaptionColor[2]),
|
||||||
|
uint8(userConfig.CaptionColor[3]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var anchor = textDrawAnchorBottom
|
||||||
|
if userConfig.CaptionPosition == "top" {
|
||||||
|
anchor = textDrawAnchorTop
|
||||||
|
}
|
||||||
|
|
||||||
|
c := freetype.NewContext()
|
||||||
|
c.SetClip(t.img.Bounds())
|
||||||
|
c.SetDPI(72)
|
||||||
|
c.SetDst(t.img)
|
||||||
|
c.SetFont(f)
|
||||||
|
c.SetHinting(font.HintingNone)
|
||||||
|
|
||||||
|
return t.drawText(c, text, textColor, userConfig.CaptionFontSize, userConfig.CaptionBorder, anchor)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t textOnImageRenderer) GetImage() image.Image { return t.img }
|
func (t textOnImageRenderer) GetImage() image.Image { return t.img }
|
||||||
|
|
||||||
func (t *textOnImageRenderer) drawText(c *freetype.Context, text string, textColor color.Color, fontsize float64, border int) error {
|
func (t *textOnImageRenderer) drawText(c *freetype.Context, text string, textColor color.Color, fontsize float64, border int, anchor textDrawAnchor) error {
|
||||||
c.SetSrc(image.NewUniform(color.RGBA{0x0, 0x0, 0x0, 0x0})) // Transparent for text size guessing
|
c.SetSrc(image.NewUniform(color.RGBA{0x0, 0x0, 0x0, 0x0})) // Transparent for text size guessing
|
||||||
|
|
||||||
textLines := strings.Split(text, "\n")
|
textLines := strings.Split(text, "\n")
|
||||||
|
@ -92,9 +132,18 @@ func (t *textOnImageRenderer) drawText(c *freetype.Context, text string, textCol
|
||||||
|
|
||||||
var (
|
var (
|
||||||
yTotal = (int(c.PointToFixed(fontsize)/64))*len(textLines) + (len(textLines)-1)*2
|
yTotal = (int(c.PointToFixed(fontsize)/64))*len(textLines) + (len(textLines)-1)*2
|
||||||
yLineTop = int(float64(sd.IconSize())/2.0 - float64(yTotal)/2.0)
|
yLineTop int
|
||||||
)
|
)
|
||||||
|
|
||||||
|
switch anchor {
|
||||||
|
case textDrawAnchorTop:
|
||||||
|
yLineTop = border
|
||||||
|
case textDrawAnchorCenter:
|
||||||
|
yLineTop = int(float64(sd.IconSize())/2.0 - float64(yTotal)/2.0)
|
||||||
|
case textDrawAnchorBottom:
|
||||||
|
yLineTop = sd.IconSize() - yTotal - border
|
||||||
|
}
|
||||||
|
|
||||||
for _, tl := range textLines {
|
for _, tl := range textLines {
|
||||||
c.SetSrc(image.NewUniform(color.RGBA{0x0, 0x0, 0x0, 0x0})) // Transparent for text size guessing
|
c.SetSrc(image.NewUniform(color.RGBA{0x0, 0x0, 0x0, 0x0})) // Transparent for text size guessing
|
||||||
ext, err := c.DrawString(tl, freetype.Pt(0, 0))
|
ext, err := c.DrawString(tl, freetype.Pt(0, 0))
|
||||||
|
@ -132,8 +181,8 @@ func (textOnImageRenderer) getImageFromDisk(filename string) (image.Image, error
|
||||||
return img, nil
|
return img, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (textOnImageRenderer) loadFont() (*truetype.Font, error) {
|
func (textOnImageRenderer) loadFont(fontfile string) (*truetype.Font, error) {
|
||||||
fontRaw, err := ioutil.ReadFile(userConfig.RenderFont)
|
fontRaw, err := ioutil.ReadFile(fontfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "Unable to read font file")
|
return nil, errors.Wrap(err, "Unable to read font file")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue