refactor code into separate files
preparation for more refactoring
This commit is contained in:
parent
49cda88dd5
commit
2a10ebd77d
|
@ -0,0 +1,33 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
_ "image/gif"
|
||||||
|
_ "image/jpeg"
|
||||||
|
"image/png"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func readImage(path string) (img image.Image) {
|
||||||
|
reader, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
img, _, err2 := image.Decode(reader)
|
||||||
|
if err2 != nil {
|
||||||
|
log.Fatal(err2)
|
||||||
|
}
|
||||||
|
return img
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeImage(path string, img image.Image) {
|
||||||
|
f, err := os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := png.Encode(f, img); err != nil {
|
||||||
|
f.Close()
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
162
main.go
162
main.go
|
@ -1,23 +1,15 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"encoding/hex"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
|
||||||
_ "image/gif"
|
|
||||||
_ "image/jpeg"
|
|
||||||
"image/png"
|
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
|
||||||
"net"
|
"net"
|
||||||
"net/textproto"
|
|
||||||
"os"
|
"os"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/SpeckiJ/Hochwasser/pixelflut"
|
||||||
)
|
)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
@ -67,23 +59,23 @@ func main() {
|
||||||
b2 := b1.Add(image.Pt(b1.Dx(), 0))
|
b2 := b1.Add(image.Pt(b1.Dx(), 0))
|
||||||
b3 := b1.Add(image.Pt(0, b1.Dy()))
|
b3 := b1.Add(image.Pt(0, b1.Dy()))
|
||||||
b4 := b1.Add(b1.Size())
|
b4 := b1.Add(b1.Size())
|
||||||
go fetchImage(fetchedImg.SubImage(b1).(*image.NRGBA))
|
go pixelflut.FetchImage(fetchedImg.SubImage(b1).(*image.NRGBA), *address)
|
||||||
go fetchImage(fetchedImg.SubImage(b2).(*image.NRGBA))
|
go pixelflut.FetchImage(fetchedImg.SubImage(b2).(*image.NRGBA), *address)
|
||||||
go fetchImage(fetchedImg.SubImage(b3).(*image.NRGBA))
|
go pixelflut.FetchImage(fetchedImg.SubImage(b3).(*image.NRGBA), *address)
|
||||||
go fetchImage(fetchedImg.SubImage(b4).(*image.NRGBA))
|
go pixelflut.FetchImage(fetchedImg.SubImage(b4).(*image.NRGBA), *address)
|
||||||
*connections -= 4
|
*connections -= 4
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate and split messages into equal chunks
|
// Generate and split messages into equal chunks
|
||||||
commands := genCommands(img, offset)
|
commands := pixelflut.CommandsFromImage(img, offset)
|
||||||
if *shuffle {
|
if *shuffle {
|
||||||
shuffleCommands(commands)
|
commands.Shuffle()
|
||||||
}
|
}
|
||||||
|
|
||||||
if *connections > 0 {
|
if *connections > 0 {
|
||||||
commandGroups := chunkCommands(commands, *connections)
|
commandGroups := commands.Chunk(*connections)
|
||||||
for _, messages := range commandGroups {
|
for _, messages := range commandGroups {
|
||||||
go bomb(messages)
|
go pixelflut.Bomb(messages, *address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,137 +90,3 @@ func main() {
|
||||||
writeImage(*fetchImgPath, fetchedImg)
|
writeImage(*fetchImgPath, fetchedImg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func bomb(messages []byte) {
|
|
||||||
conn, err := net.Dial("tcp", *address)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
// Start bombardement
|
|
||||||
for {
|
|
||||||
_, err := conn.Write(messages)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func readImage(path string) (img image.Image) {
|
|
||||||
reader, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
img, _, err2 := image.Decode(reader)
|
|
||||||
if err2 != nil {
|
|
||||||
log.Fatal(err2)
|
|
||||||
}
|
|
||||||
|
|
||||||
return img
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeImage(path string, img image.Image) {
|
|
||||||
f, err := os.Create(path)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := png.Encode(f, img); err != nil {
|
|
||||||
f.Close()
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates message based on given image
|
|
||||||
func genCommands(img image.Image, offset image.Point) (commands [][]byte) {
|
|
||||||
b := img.Bounds()
|
|
||||||
commands = 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++ {
|
|
||||||
// ensure we're working with RGBA colors (non-alpha-pre-multiplied)
|
|
||||||
c := color.NRGBAModel.Convert(img.At(x, y)).(color.NRGBA)
|
|
||||||
|
|
||||||
// ignore transparent pixels
|
|
||||||
if c.A == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// @incomplete: also send alpha? -> bandwidth tradeoff
|
|
||||||
// @speed: this sprintf call is quite slow..
|
|
||||||
cmd := fmt.Sprintf("PX %d %d %.2x%.2x%.2x\n",
|
|
||||||
x + offset.X, y + offset.Y, c.R, c.G, c.B)
|
|
||||||
commands[numCmds] = []byte(cmd)
|
|
||||||
numCmds++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return commands[:numCmds]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Splits messages into equally sized chunks
|
|
||||||
func chunkCommands(commands [][]byte, numChunks int) [][]byte {
|
|
||||||
chunks := make([][]byte, numChunks)
|
|
||||||
|
|
||||||
chunkLength := len(commands) / numChunks
|
|
||||||
for i := 0; i < numChunks; i++ {
|
|
||||||
cmdOffset := i * chunkLength
|
|
||||||
for j := 0; j < chunkLength; j++ {
|
|
||||||
chunks[i] = append(chunks[i], commands[cmdOffset+j]...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return chunks
|
|
||||||
}
|
|
||||||
|
|
||||||
func shuffleCommands(slice [][]byte) {
|
|
||||||
for i := range slice {
|
|
||||||
j := rand.Intn(i + 1)
|
|
||||||
slice[i], slice[j] = slice[j], slice[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchImage(img *image.NRGBA) {
|
|
||||||
// FIXME @speed: this is unusably s l o w w w
|
|
||||||
// bottleneck seems to be our pixel reading/parsing code. cpuprofile!
|
|
||||||
// -> should buffer it just as in bomb()
|
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", *address)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
reader := bufio.NewReader(conn)
|
|
||||||
tp := textproto.NewReader(reader)
|
|
||||||
|
|
||||||
b := img.Bounds()
|
|
||||||
for {
|
|
||||||
for x := b.Min.X; x < b.Max.X; x++ {
|
|
||||||
for y := b.Min.Y; y < b.Max.Y; y++ {
|
|
||||||
// request pixel
|
|
||||||
fmt.Fprintf(conn, "PX %d %d\n", x, y)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// read pixel
|
|
||||||
// @speed try to run this in a separate goroutine?
|
|
||||||
// we probably need to buffer the responses then
|
|
||||||
res, err := tp.ReadLine()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
res2 := strings.Split(res, " ")
|
|
||||||
col, _ := hex.DecodeString(res2[3])
|
|
||||||
img.Set(x, y, color.NRGBA{
|
|
||||||
uint8(col[0]),
|
|
||||||
uint8(col[1]),
|
|
||||||
uint8(col[2]),
|
|
||||||
255,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package pixelflut
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Commands represent a list of messages to be sent to a pixelflut server.
|
||||||
|
type Commands [][]byte
|
||||||
|
|
||||||
|
// Chunk splits commands into equally sized chunks, while flattening each chunk
|
||||||
|
// so that all commands are concatenated as a single `[]byte`.
|
||||||
|
func (c Commands) Chunk(numChunks int) [][]byte {
|
||||||
|
chunks := make([][]byte, numChunks)
|
||||||
|
chunkLength := len(c) / numChunks
|
||||||
|
for i := 0; i < numChunks; i++ {
|
||||||
|
cmdOffset := i * chunkLength
|
||||||
|
for j := 0; j < chunkLength; j++ {
|
||||||
|
chunks[i] = append(chunks[i], c[cmdOffset+j]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return chunks
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shuffle reorders commands randomly, in place.
|
||||||
|
func (c Commands) Shuffle() {
|
||||||
|
for i := range c {
|
||||||
|
j := rand.Intn(i + 1)
|
||||||
|
c[i], c[j] = c[j], c[i]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package pixelflut
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/textproto"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FetchImage(img *image.NRGBA, address string) {
|
||||||
|
// FIXME @speed: this is unusably s l o w w w
|
||||||
|
// bottleneck seems to be our pixel reading/parsing code. cpuprofile!
|
||||||
|
// -> should buffer it just as in bomb()
|
||||||
|
|
||||||
|
conn, err := net.Dial("tcp", address)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
reader := bufio.NewReader(conn)
|
||||||
|
tp := textproto.NewReader(reader)
|
||||||
|
|
||||||
|
b := img.Bounds()
|
||||||
|
for {
|
||||||
|
for x := b.Min.X; x < b.Max.X; x++ {
|
||||||
|
for y := b.Min.Y; y < b.Max.Y; y++ {
|
||||||
|
// request pixel
|
||||||
|
fmt.Fprintf(conn, "PX %d %d\n", x, y)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// read pixel
|
||||||
|
// @speed try to run this in a separate goroutine?
|
||||||
|
// we probably need to buffer the responses then
|
||||||
|
res, err := tp.ReadLine()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
res2 := strings.Split(res, " ")
|
||||||
|
col, _ := hex.DecodeString(res2[3])
|
||||||
|
img.Set(x, y, color.NRGBA{
|
||||||
|
uint8(col[0]),
|
||||||
|
uint8(col[1]),
|
||||||
|
uint8(col[2]),
|
||||||
|
255,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package pixelflut
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bomb writes the given message via plain TCP to the given address,
|
||||||
|
// forever, as fast as possible.
|
||||||
|
func Bomb(message []byte, address string) {
|
||||||
|
conn, err := net.Dial("tcp", address)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
_, err := conn.Write(message)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package pixelflut
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CommandsFromImage converts an image to the respective pixelflut commands
|
||||||
|
func CommandsFromImage(img image.Image, offset image.Point) (commands Commands) {
|
||||||
|
b := img.Bounds()
|
||||||
|
commands = 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++ {
|
||||||
|
// ensure we're working with RGBA colors (non-alpha-pre-multiplied)
|
||||||
|
c := color.NRGBAModel.Convert(img.At(x, y)).(color.NRGBA)
|
||||||
|
|
||||||
|
// ignore transparent pixels
|
||||||
|
if c.A == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// @incomplete: also send alpha? -> bandwidth tradeoff
|
||||||
|
// @speed: this sprintf call is quite slow..
|
||||||
|
cmd := fmt.Sprintf("PX %d %d %.2x%.2x%.2x\n",
|
||||||
|
x + offset.X, y + offset.Y, c.R, c.G, c.B)
|
||||||
|
commands[numCmds] = []byte(cmd)
|
||||||
|
numCmds++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return commands[:numCmds]
|
||||||
|
}
|
Loading…
Reference in New Issue