diff --git a/README.md b/README.md index f70e8d4..11fdfcd 100644 --- a/README.md +++ b/README.md @@ -21,4 +21,12 @@ the top left tile. slicerdicer --help - slicerdicer --filename foo.png --tile-size 256 + slicerdicer --filename foo.png --tile-size 256 --concurrency 5 + +## Notes + +It's going to eat some memory. + +In my tests on an 32641 x 16471, 8-bit/color RGB PNG, memory usage peaks at +around 2.7GB. + diff --git a/main.go b/main.go index fd3500a..8814c74 100644 --- a/main.go +++ b/main.go @@ -5,15 +5,14 @@ import "image/png" import "github.com/disintegration/imaging" import "runtime" import "flag" - - import "fmt" import "os" func main() { - filenamePtr := flag.String("filename", "screenshot.png", "filename to open") - tile_size := flag.Int ("tile-size", 512, "tile size, in pixels") + filenamePtr := flag.String("filename", "screenshot.png", "filename to open") + tileSizePtr := flag.Int ("tile-size", 512, "tile size, in pixels") + concurrencyPtr := flag.Int ("concurrency", 5, "how many tiles to generate concurrently (threads)") flag.Parse() @@ -26,20 +25,24 @@ func main() { size := src.Bounds().Max - tile_size_x := *tile_size - tile_size_y := *tile_size - - fmt.Println("starting tiling") + tile_size_x := *tileSizePtr + tile_size_y := *tileSizePtr z := 0 + concurrency := *concurrencyPtr + sem := make(chan bool, concurrency) + + fmt.Println("starting tiling with concurrency of", concurrency) + // outer loop for zoom for { if (z == 0) { // do nothing } else { // halve image size - src = imaging.Resize(src, size.X/2, 0, imaging.Linear) + src = imaging.Resize(src, size.X/2, 0, imaging.NearestNeighbor) + runtime.GC() // recalculate size size = src.Bounds().Max // we are done if we are now smaller then the tile @@ -51,29 +54,37 @@ func main() { fmt.Print(fmt.Sprintf("zoom level: %d (%d x %d)\n", z, size.X, size.Y)) for y := 0 ; y <= (size.Y / tile_size_y) ; y++ { - for x := 0 ; x <= (size.X / tile_size_x) ; x++ { - - output_filename := fmt.Sprintf("tile-%d-%d-%d.png", z, x, y) - cropped := imaging.Crop(src, image.Rect(tile_size_x*x, tile_size_y*y, tile_size_x*x+tile_size_x, tile_size_y*y+tile_size_y)); - - fmt.Print("writing to: ", output_filename, " "); - fmt.Print("\r") - - writer, _ := os.Create(output_filename) - err = png.Encode(writer, cropped) - writer.Close() - runtime.GC() - if err != nil { - fmt.Println(err) - } + sem <- true + go tile(src, z, x, y, tile_size_x, tile_size_y, sem) } + } - fmt.Print("\r \r") z++ } - fmt.Println("done") + // drain at the end of each zoom level + // since we are about to modify the source image + for i := 0; i < cap(sem); i++ { + sem <- true + } + fmt.Println("done") +} + +func tile (src image.Image, z, x, y int, tile_size_x, tile_size_y int, sem chan bool) { + defer func() { <-sem }() + output_filename := fmt.Sprintf("tile-%d-%d-%d.png", z, x, y) + cropped := imaging.Crop(src, image.Rect(tile_size_x*x, tile_size_y*y, tile_size_x*x+tile_size_x, tile_size_y*y+tile_size_y)); + + writer, _ := os.Create(output_filename) + err := png.Encode(writer, cropped) + if err != nil { + fmt.Println(err) + } + writer.Close() + + runtime.GC() + return; }