parent
							
								
									474901fbd5
								
							
						
					
					
						commit
						3ac85ca3f0
					
				
							
								
								
									
										9
									
								
								main.go
								
								
								
								
							
							
						
						
									
										9
									
								
								main.go
								
								
								
								
							| 
						 | 
				
			
			@ -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,
 | 
			
		||||
		})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										22
									
								
								rpc/repl.go
								
								
								
								
							
							
						
						
									
										22
									
								
								rpc/repl.go
								
								
								
								
							| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue