screen1d: import from periph/extra

Tweak it a bit to expose the palette as an option.
Uses the same license.
This is the first commit that causes a dependency on golang.org/x/sys.
pull/14/head
Marc-Antoine Ruel 5 years ago
parent fa6bf2a91f
commit 94908ff801

@ -9,6 +9,8 @@ go 1.13
require (
github.com/fogleman/gg v1.3.0
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/maruel/ansi256 v1.0.2
github.com/mattn/go-colorable v0.1.8
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb
periph.io/x/conn/v3 v3.6.7
periph.io/x/host/v3 v3.6.7

@ -2,8 +2,17 @@ github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/maruel/ansi256 v1.0.2 h1:AE5gYrrZ5vQaFTTwy5vxva8Bak7p7wID3Uqu3t1j3No=
github.com/maruel/ansi256 v1.0.2/go.mod h1:x7uow2KFkUgjdzvYHyfZuMEOTGKvCYLyVUHIVg1vYic=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb h1:fqpd0EBDzlHRCjiphRR5Zo/RSWWQlWv34418dnEixWk=
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
periph.io/x/conn/v3 v3.6.7 h1:hem/gzoUI0tnvdJOJAk+XLBhqBGX9sHkwShBXRGGy0k=
periph.io/x/conn/v3 v3.6.7/go.mod h1:3OD27w9YVa5DS97VsUxsPGzD9Qrm5Ny7cF5b6xMMIWg=

@ -0,0 +1,131 @@
// Copyright 2017 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
// Package screen1d implements a 1D display.Drawer that outputs to terminal
// (stdout) using ANSI color codes.
//
// Useful while you are waiting for your super nice APA-102 LED strip to come
// by mail.
package screen1d
import (
"bytes"
"errors"
"fmt"
"image"
"image/color"
"io"
"github.com/maruel/ansi256"
"github.com/mattn/go-colorable"
"periph.io/x/conn/v3/display"
)
// Opts represents the options available for this display.
type Opts struct {
X int
Palette *ansi256.Palette
}
// Dev is a 1D LED strip emulator that outputs to the console.
type Dev struct {
w io.Writer
l int
palette ansi256.Palette
pixels []byte
buf bytes.Buffer
}
// New returns a Dev that displays at the console.
//
// Permits to do local testing of LEDs animation.
func New(opts *Opts) *Dev {
p := opts.Palette
if p == nil {
p = ansi256.Default
}
d := &Dev{
w: colorable.NewColorableStdout(),
l: opts.X,
palette: *p,
pixels: make([]byte, 3*opts.X),
}
return d
}
func (d *Dev) String() string {
return "Screen1D"
}
// Halt implements conn.Resource.
//
// It clears the display so it is not corrupted.
func (d *Dev) Halt() error {
_, err := d.w.Write([]byte("\n\033[0m"))
if err != nil {
return err
}
return nil
}
// Write accepts a stream of raw RGB pixels and writes it to the console.
func (d *Dev) Write(pixels []byte) (int, error) {
if len(pixels)%3 != 0 {
return 0, errors.New("invalid RGB stream length")
}
copy(d.pixels, pixels)
return d.refresh()
}
// ColorModel implements display.Drawer.
func (d *Dev) ColorModel() color.Model {
return color.NRGBAModel
}
// Bounds implements display.Drawer.
func (d *Dev) Bounds() image.Rectangle {
return image.Rectangle{Max: image.Point{X: d.l, Y: 1}}
}
// Draw implements display.Drawer.
func (d *Dev) Draw(r image.Rectangle, src image.Image, sp image.Point) error {
r = r.Intersect(d.Bounds())
srcR := src.Bounds()
srcR.Min = srcR.Min.Add(sp)
if dX := r.Dx(); dX < srcR.Dx() {
srcR.Max.X = srcR.Min.X + dX
}
if dY := r.Dy(); dY < srcR.Dy() {
srcR.Max.Y = srcR.Min.Y + dY
}
// TODO(maruel): Allow non-full screen drawing.
// Generic version.
deltaX3 := 3 * (r.Min.X - srcR.Min.X)
for sX := srcR.Min.X; sX < srcR.Max.X; sX++ {
r16, g16, b16, _ := src.At(sX, srcR.Min.Y).RGBA()
dX3 := 3*sX + deltaX3
d.pixels[dX3] = byte(r16 >> 8)
d.pixels[dX3+1] = byte(g16 >> 8)
d.pixels[dX3+2] = byte(b16 >> 8)
}
_, err := d.refresh()
return err
}
func (d *Dev) refresh() (int, error) {
// This code is designed to minimize the amount of memory allocated per call.
d.buf.Reset()
_, _ = d.buf.WriteString("\r\033[0m")
for i := 0; i < len(d.pixels)/3; i++ {
c := color.NRGBA{d.pixels[3*i], d.pixels[3*i+1], d.pixels[3*i+2], 255}
_, _ = io.WriteString(&d.buf, d.palette.Block(c))
}
_, _ = d.buf.WriteString("\033[0m ")
_, err := d.buf.WriteTo(d.w)
return len(d.pixels), err
}
var _ display.Drawer = &Dev{}
var _ fmt.Stringer = &Dev{}
Loading…
Cancel
Save