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")
x = flag.Int("x", 0, "Offset of posted image from left 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")
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{
FlutTaskOpts: pixelflut.FlutTaskOpts{
Address: *address,
MaxConns: *connections,
Offset: image.Pt(*x, *y),
Shuffle: true,
Address: *address,
MaxConns: *connections,
Offset: image.Pt(*x, *y),
RenderOrder: pixelflut.NewOrder(*order),
},
Img: img,
})

View File

@ -38,15 +38,35 @@ func OffsetCmd(x, y int) []byte {
}
// 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()
cmds = make([][]byte, b.Size().X*b.Size().Y)
numCmds := 0
for x := b.Min.X; x < b.Max.X; x++ {
for y := b.Min.Y; y < b.Max.Y; y++ {
max1 := b.Max.X
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)
// ignore transparent pixels
if c.A == 0 {
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) {

View File

@ -18,13 +18,13 @@ type FlutTask struct {
// FlutTaskOpts specifies parameters of the flut
type FlutTaskOpts struct {
Address string
MaxConns int
Offset image.Point
Paused bool
Shuffle bool
RGBSplit bool
RandOffset bool
Address string
MaxConns int
Offset image.Point
Paused bool
RGBSplit bool
RandOffset bool
RenderOrder RenderOrder
}
// 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()
}
return fmt.Sprintf(
" %d conns @ %s\n img %v offset %v\n shuffle %v rgbsplit %v randoffset %v paused %v",
t.MaxConns, t.Address, img, t.Offset, t.Shuffle, t.RGBSplit, t.RandOffset, t.Paused,
" %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.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
}
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`
// using `conns` connections. Pixels are sent column wise, unless `shuffle`
// 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?
}
var cmds commands
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()
}
cmds := generateCommands(t)
var messages [][]byte
var maxOffsetX, maxOffsetY int
@ -91,7 +103,7 @@ func Flut(t FlutTask, stop chan bool, wg *sync.WaitGroup) {
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)
}
@ -100,3 +112,17 @@ func Flut(t FlutTask, stop chan bool, wg *sync.WaitGroup) {
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 {
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":
t.Paused = false
case "offset":
case "offset", "of":
if len(args) == 1 && args[0] == "rand" {
t.RandOffset = true
t.Offset = image.Point{}
@ -79,20 +79,22 @@ func RunREPL(f Fluter) {
}
}
case "conns":
case "connections", "c":
if len(args) == 1 {
if conns, err := strconv.Atoi(args[0]); err == nil {
t.MaxConns = conns
}
}
case "addr":
case "address", "a":
if len(args) == 1 {
t.Address = args[0]
}
case "shuffle":
t.Shuffle = !t.Shuffle
case "order", "o":
if len(args) == 1 {
t.RenderOrder = pixelflut.NewOrder(args[0])
}
case "rgbsplit":
t.RGBSplit = !t.RGBSplit
@ -118,7 +120,7 @@ func RunREPL(f Fluter) {
t.Img = render.RenderText(input, textSize, textCol, bgCol)
}
case "img":
case "img", "i":
if len(args) > 0 {
path := strings.Join(args, " ")
if img, err := render.ReadImage(path); err != nil {
@ -151,17 +153,17 @@ func printHelp() {
fmt.Println(`available commands:
start start fluting
stop pause fluting
conns <n> set number of connections per client
addr <host>:<port> set target server
c <n> set number of connections per client
a <host>:<port> set target server
offset <x> <y> set top-left offset
offset rand random offset for each draw
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>]] enter interactive text mode
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,