refactor code into separate files

preparation for more refactoring
This commit is contained in:
Norwin Roosen 2020-02-05 19:26:43 +01:00
parent 49cda88dd5
commit 2a10ebd77d
6 changed files with 187 additions and 152 deletions

33
io.go Normal file
View File

@ -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
View File

@ -1,23 +1,15 @@
package main
import (
"bufio"
"encoding/hex"
"flag"
"fmt"
"image"
"image/color"
_ "image/gif"
_ "image/jpeg"
"image/png"
"log"
"math/rand"
"net"
"net/textproto"
"os"
"runtime/pprof"
"strings"
"time"
"github.com/SpeckiJ/Hochwasser/pixelflut"
)
var err error
@ -67,23 +59,23 @@ func main() {
b2 := b1.Add(image.Pt(b1.Dx(), 0))
b3 := b1.Add(image.Pt(0, b1.Dy()))
b4 := b1.Add(b1.Size())
go fetchImage(fetchedImg.SubImage(b1).(*image.NRGBA))
go fetchImage(fetchedImg.SubImage(b2).(*image.NRGBA))
go fetchImage(fetchedImg.SubImage(b3).(*image.NRGBA))
go fetchImage(fetchedImg.SubImage(b4).(*image.NRGBA))
go pixelflut.FetchImage(fetchedImg.SubImage(b1).(*image.NRGBA), *address)
go pixelflut.FetchImage(fetchedImg.SubImage(b2).(*image.NRGBA), *address)
go pixelflut.FetchImage(fetchedImg.SubImage(b3).(*image.NRGBA), *address)
go pixelflut.FetchImage(fetchedImg.SubImage(b4).(*image.NRGBA), *address)
*connections -= 4
}
// Generate and split messages into equal chunks
commands := genCommands(img, offset)
commands := pixelflut.CommandsFromImage(img, offset)
if *shuffle {
shuffleCommands(commands)
commands.Shuffle()
}
if *connections > 0 {
commandGroups := chunkCommands(commands, *connections)
commandGroups := commands.Chunk(*connections)
for _, messages := range commandGroups {
go bomb(messages)
go pixelflut.Bomb(messages, *address)
}
}
@ -98,137 +90,3 @@ func main() {
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,
})
}
}
}
}

30
pixelflut/commands.go Normal file
View File

@ -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]
}
}

57
pixelflut/fetch.go Normal file
View File

@ -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,
})
}
}
}
}

23
pixelflut/net.go Normal file
View File

@ -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)
}
}
}

34
pixelflut/send.go Normal file
View File

@ -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]
}