implement all te draw orderz

& add shorthands in REPL
This commit is contained in:
Norwin Roosen 2021-01-01 04:04:11 +01:00
parent 230a3d3f24
commit 4860070a6b
No known key found for this signature in database
GPG Key ID: 24BC059DE24C43A3
5 changed files with 102 additions and 46 deletions

View File

@ -25,6 +25,7 @@ var (
connections = flag.Int("connections", 4, "Number of simultaneous connections. Each connection posts a subimage") connections = flag.Int("connections", 4, "Number of simultaneous connections. Each connection posts a subimage")
x = flag.Int("x", 0, "Offset of posted image from left border") x = flag.Int("x", 0, "Offset of posted image from left border")
y = flag.Int("y", 0, "Offset of posted image from top border") y = flag.Int("y", 0, "Offset of posted image from top border")
order = flag.String("order", "rtl", "Draw order (shuffle, ltr, rtl, ttb, btt)")
fetchImgPath = flag.String("fetch", "", "Enable fetching the screen area to the given local file, updating it each second") fetchImgPath = flag.String("fetch", "", "Enable fetching the screen area to the given local file, updating it each second")
cpuprofile = flag.String("cpuprofile", "", "Destination file for CPU Profile") cpuprofile = flag.String("cpuprofile", "", "Destination file for CPU Profile")
) )
@ -72,10 +73,10 @@ func taskFromFlags(stop chan bool, wg *sync.WaitGroup) {
r.SetTask(pixelflut.FlutTask{ r.SetTask(pixelflut.FlutTask{
FlutTaskOpts: pixelflut.FlutTaskOpts{ FlutTaskOpts: pixelflut.FlutTaskOpts{
Address: *address, Address: *address,
MaxConns: *connections, MaxConns: *connections,
Offset: image.Pt(*x, *y), Offset: image.Pt(*x, *y),
Shuffle: true, RenderOrder: pixelflut.NewOrder(*order),
}, },
Img: img, Img: img,
}) })

View File

@ -38,15 +38,35 @@ func OffsetCmd(x, y int) []byte {
} }
// CommandsFromImage converts an image to the respective pixelflut commands // CommandsFromImage converts an image to the respective pixelflut commands
func commandsFromImage(img *image.NRGBA, offset image.Point) (cmds commands) { func commandsFromImage(img *image.NRGBA, order RenderOrder, offset image.Point) (cmds commands) {
b := img.Bounds() b := img.Bounds()
cmds = make([][]byte, b.Size().X*b.Size().Y) cmds = make([][]byte, b.Size().X*b.Size().Y)
numCmds := 0 numCmds := 0
for x := b.Min.X; x < b.Max.X; x++ { max1 := b.Max.X
for y := b.Min.Y; y < b.Max.Y; y++ { max2 := b.Max.Y
min1 := b.Min.X
min2 := b.Min.Y
dir := 1
if order.IsVertical() {
max1, max2 = max2, max1
min1, min2 = min2, min1
}
if order.IsReverse() {
min1, max1 = max1, min1
min2, max2 = max2, min2
dir *= -1
}
for i1 := min1; i1 != max1; i1 += dir {
for i2 := min2; i2 != max2; i2 += dir {
x := i1
y := i2
if order.IsVertical() {
x, y = y, x
}
c := img.At(x, y).(color.NRGBA) c := img.At(x, y).(color.NRGBA)
// ignore transparent pixels
if c.A == 0 { if c.A == 0 {
continue continue
} }
@ -64,7 +84,13 @@ func commandsFromImage(img *image.NRGBA, offset image.Point) (cmds commands) {
} }
} }
return cmds[:numCmds] cmds = cmds[:numCmds]
if order == Shuffle {
cmds.Shuffle()
}
return
} }
func cmdsFetchImage(bounds image.Rectangle) (cmds commands) { func cmdsFetchImage(bounds image.Rectangle) (cmds commands) {

View File

@ -18,13 +18,13 @@ type FlutTask struct {
// FlutTaskOpts specifies parameters of the flut // FlutTaskOpts specifies parameters of the flut
type FlutTaskOpts struct { type FlutTaskOpts struct {
Address string Address string
MaxConns int MaxConns int
Offset image.Point Offset image.Point
Paused bool Paused bool
Shuffle bool RGBSplit bool
RGBSplit bool RandOffset bool
RandOffset bool RenderOrder RenderOrder
} }
// FlutTaskData contains the actual pixeldata to flut, separated because of size // FlutTaskData contains the actual pixeldata to flut, separated because of size
@ -36,8 +36,8 @@ func (t FlutTask) String() string {
img = t.Img.Bounds().Size().String() img = t.Img.Bounds().Size().String()
} }
return fmt.Sprintf( return fmt.Sprintf(
" %d conns @ %s\n img %v offset %v\n shuffle %v rgbsplit %v randoffset %v paused %v", " %d conns @ %s\n img %v offset %v\n order %s rgbsplit %v randoffset %v paused %v",
t.MaxConns, t.Address, img, t.Offset, t.Shuffle, t.RGBSplit, t.RandOffset, t.Paused, t.MaxConns, t.Address, img, t.Offset, t.RenderOrder, t.RGBSplit, t.RandOffset, t.Paused,
) )
} }
@ -46,6 +46,34 @@ func (t FlutTask) IsFlutable() bool {
return t.Img != nil && t.MaxConns > 0 && t.Address != "" && !t.Paused return t.Img != nil && t.MaxConns > 0 && t.Address != "" && !t.Paused
} }
type RenderOrder uint8
func (t RenderOrder) String() string { return []string{"→", "↓", "←", "↑", "random"}[t] }
func (t RenderOrder) IsVertical() bool { return t&0b01 != 0 }
func (t RenderOrder) IsReverse() bool { return t&0b10 != 0 }
func NewOrder(v string) RenderOrder {
switch v {
case "ltr", "l", "→":
return LeftToRight
case "rtl", "r", "←":
return RightToLeft
case "ttb", "t", "↓":
return TopToBottom
case "btt", "b", "↑":
return BottomToTop
default:
return Shuffle
}
}
const (
LeftToRight = 0b000
TopToBottom = 0b001
RightToLeft = 0b010
BottomToTop = 0b011
Shuffle = 0b100
)
// Flut asynchronously sends the given image to pixelflut server at `address` // Flut asynchronously sends the given image to pixelflut server at `address`
// using `conns` connections. Pixels are sent column wise, unless `shuffle` // using `conns` connections. Pixels are sent column wise, unless `shuffle`
// is set. Stops when stop is closed. // is set. Stops when stop is closed.
@ -55,23 +83,7 @@ func Flut(t FlutTask, stop chan bool, wg *sync.WaitGroup) {
return // @robustness: actually return an error here? return // @robustness: actually return an error here?
} }
var cmds commands cmds := generateCommands(t)
if t.RGBSplit {
// do a RGB split of white
imgmod := render.ImgColorFilter(t.Img, color.NRGBA{0xff, 0xff, 0xff, 0xff}, color.NRGBA{0xff, 0, 0, 0xff})
cmds = append(cmds, commandsFromImage(imgmod, image.Pt(t.Offset.X-10, t.Offset.Y-10))...)
imgmod = render.ImgColorFilter(t.Img, color.NRGBA{0xff, 0xff, 0xff, 0xff}, color.NRGBA{0, 0xff, 0, 0xff})
cmds = append(cmds, commandsFromImage(imgmod, image.Pt(t.Offset.X+10, t.Offset.Y))...)
imgmod = render.ImgColorFilter(t.Img, color.NRGBA{0xff, 0xff, 0xff, 0xff}, color.NRGBA{0, 0, 0xff, 0xff})
cmds = append(cmds, commandsFromImage(imgmod, image.Pt(t.Offset.X-10, t.Offset.Y+10))...)
cmds = append(cmds, commandsFromImage(t.Img, t.Offset)...)
} else {
cmds = commandsFromImage(t.Img, t.Offset)
}
if t.Shuffle {
cmds.Shuffle()
}
var messages [][]byte var messages [][]byte
var maxOffsetX, maxOffsetY int var maxOffsetX, maxOffsetY int
@ -91,7 +103,7 @@ func Flut(t FlutTask, stop chan bool, wg *sync.WaitGroup) {
msg = messages[i] msg = messages[i]
} }
time.Sleep(66 * time.Millisecond) // avoid crashing the server time.Sleep(50 * time.Millisecond) // avoid crashing the server
go bombAddress(msg, t.Address, maxOffsetX, maxOffsetY, stop, &bombWg) go bombAddress(msg, t.Address, maxOffsetX, maxOffsetY, stop, &bombWg)
} }
@ -100,3 +112,17 @@ func Flut(t FlutTask, stop chan bool, wg *sync.WaitGroup) {
wg.Done() wg.Done()
} }
} }
func generateCommands(t FlutTask) (cmds commands) {
if t.RGBSplit {
white := color.NRGBA{0xff, 0xff, 0xff, 0xff}
imgmod := render.ImgColorFilter(t.Img, white, color.NRGBA{0xff, 0, 0, 0xff})
cmds = append(cmds, commandsFromImage(imgmod, t.RenderOrder, t.Offset.Add(image.Pt(-10, -10)))...)
imgmod = render.ImgColorFilter(t.Img, white, color.NRGBA{0, 0xff, 0, 0xff})
cmds = append(cmds, commandsFromImage(imgmod, t.RenderOrder, t.Offset.Add(image.Pt(10, 0)))...)
imgmod = render.ImgColorFilter(t.Img, white, color.NRGBA{0, 0, 0xff, 0xff})
cmds = append(cmds, commandsFromImage(imgmod, t.RenderOrder, t.Offset.Add(image.Pt(-10, 10)))...)
}
cmds = append(cmds, commandsFromImage(t.Img, t.RenderOrder, t.Offset)...)
return
}

View File

@ -121,6 +121,7 @@ func bombAddress(message []byte, address string, maxOffsetX, maxOffsetY int, sto
if err == nil { if err == nil {
break // we're supposed to exit break // we're supposed to exit
} }
fmt.Printf("[net] error: %s\n", err)
} }
} }

View File

@ -66,7 +66,7 @@ func RunREPL(f Fluter) {
case "start": case "start":
t.Paused = false t.Paused = false
case "offset": case "offset", "of":
if len(args) == 1 && args[0] == "rand" { if len(args) == 1 && args[0] == "rand" {
t.RandOffset = true t.RandOffset = true
t.Offset = image.Point{} t.Offset = image.Point{}
@ -79,20 +79,22 @@ func RunREPL(f Fluter) {
} }
} }
case "conns": case "connections", "c":
if len(args) == 1 { if len(args) == 1 {
if conns, err := strconv.Atoi(args[0]); err == nil { if conns, err := strconv.Atoi(args[0]); err == nil {
t.MaxConns = conns t.MaxConns = conns
} }
} }
case "addr": case "address", "a":
if len(args) == 1 { if len(args) == 1 {
t.Address = args[0] t.Address = args[0]
} }
case "shuffle": case "order", "o":
t.Shuffle = !t.Shuffle if len(args) == 1 {
t.RenderOrder = pixelflut.NewOrder(args[0])
}
case "rgbsplit": case "rgbsplit":
t.RGBSplit = !t.RGBSplit t.RGBSplit = !t.RGBSplit
@ -118,7 +120,7 @@ func RunREPL(f Fluter) {
t.Img = render.RenderText(input, textSize, textCol, bgCol) t.Img = render.RenderText(input, textSize, textCol, bgCol)
} }
case "img": case "img", "i":
if len(args) > 0 { if len(args) > 0 {
path := strings.Join(args, " ") path := strings.Join(args, " ")
if img, err := render.ReadImage(path); err != nil { if img, err := render.ReadImage(path); err != nil {
@ -151,17 +153,17 @@ func printHelp() {
fmt.Println(`available commands: fmt.Println(`available commands:
start start fluting start start fluting
stop pause fluting stop pause fluting
conns <n> set number of connections per client c <n> set number of connections per client
addr <host>:<port> set target server a <host>:<port> set target server
offset <x> <y> set top-left offset offset <x> <y> set top-left offset
offset rand random offset for each draw offset rand random offset for each draw
metrics toggle bandwidth reporting (may cost some performance) metrics toggle bandwidth reporting (may cost some performance)
img <filepath> set image i <filepath> set image
txt <scale> <color <bgcolor> <txt> send text txt <scale> <color <bgcolor> <txt> send text
txt [<scale> [<color> [<bgcolor>]] enter interactive text mode txt [<scale> [<color> [<bgcolor>]] enter interactive text mode
rgbsplit toggle RGB split effect rgbsplit toggle RGB split effect
shuffle toggle between column-wise & randomized draw order`) o set order (l,r,t,b,shuffle)`)
} }
// try to parse as hex-encoded RGB color, // try to parse as hex-encoded RGB color,