Remove pre-render mode from stitcher & Cleanup

This commit is contained in:
David Vogel 2022-08-10 21:04:17 +02:00
parent cd1428706e
commit 7d250d6405
6 changed files with 38 additions and 95 deletions

View File

@ -14,9 +14,11 @@
"gridify", "gridify",
"hacky", "hacky",
"hilbertify", "hilbertify",
"Hitbox",
"ipairs", "ipairs",
"kbinani", "kbinani",
"Lanczos", "Lanczos",
"lann",
"ldflags", "ldflags",
"linearize", "linearize",
"lowram", "lowram",
@ -25,7 +27,6 @@
"nfnt", "nfnt",
"noita", "noita",
"polymorphed", "polymorphed",
"prerender",
"promptui", "promptui",
"rasterizer", "rasterizer",
"savegames", "savegames",

View File

@ -46,8 +46,6 @@ example list of files:
Lower bound of the output rectangle. This coordinate is not included in the output. Lower bound of the output rectangle. This coordinate is not included in the output.
- `ymin int` - `ymin int`
Upper bound of the output rectangle. This coordinate is included in the output. Upper bound of the output rectangle. This coordinate is included in the output.
- `prerender`
Pre renders the image in RAM before saving. Can speed things up if you have enough RAM.
- `cleanup float` - `cleanup float`
Enables cleanup mode with the given float as threshold. This will **DELETE** images from the input folder; no stitching will be done in this mode. A good value to start with is `0.999`, which deletes images where the sum of the min-max difference of each sub-pixel overlapping with other images is less than 99.9%% of the maximum possible sum of pixel differences. Enables cleanup mode with the given float as threshold. This will **DELETE** images from the input folder; no stitching will be done in this mode. A good value to start with is `0.999`, which deletes images where the sum of the min-max difference of each sub-pixel overlapping with other images is less than 99.9%% of the maximum possible sum of pixel differences.

View File

@ -8,14 +8,13 @@ package main
import ( import (
"encoding/json" "encoding/json"
"image/color" "image/color"
"log"
"os" "os"
"github.com/tdewolff/canvas" "github.com/tdewolff/canvas"
) )
var entityDisplayFontFamily = canvas.NewFontFamily("times") //var entityDisplayFontFamily = canvas.NewFontFamily("times")
var entityDisplayFontFace *canvas.FontFace //var entityDisplayFontFace *canvas.FontFace
var entityDisplayAreaDamageStyle = canvas.Style{ var entityDisplayAreaDamageStyle = canvas.Style{
FillColor: color.RGBA{100, 0, 0, 100}, FillColor: color.RGBA{100, 0, 0, 100},
@ -73,13 +72,13 @@ var entityDisplayCollisionTriggerStyle = canvas.Style{
} }
func init() { func init() {
fontName := "NimbusRoman-Regular" //fontName := "NimbusRoman-Regular"
if err := entityDisplayFontFamily.LoadLocalFont(fontName, canvas.FontRegular); err != nil { //if err := entityDisplayFontFamily.LoadLocalFont(fontName, canvas.FontRegular); err != nil {
log.Printf("Couldn't load font %q: %v", fontName, err) // log.Printf("Couldn't load font %q: %v", fontName, err)
} //}
entityDisplayFontFace = entityDisplayFontFamily.Face(48.0, canvas.White, canvas.FontRegular, canvas.FontNormal) //entityDisplayFontFace = entityDisplayFontFamily.Face(48.0, canvas.White, canvas.FontRegular, canvas.FontNormal)
} }
type Entity struct { type Entity struct {
@ -146,6 +145,7 @@ func (e Entity) Draw(c *canvas.Context) {
if member, ok := component.Members["circle_radius"]; ok { if member, ok := component.Members["circle_radius"]; ok {
if radius, ok := member.(float64); ok && radius > 0 { if radius, ok := member.(float64); ok && radius > 0 {
// Theoretically we need to clip the damage area to the intersection of the AABB and the circle, but meh. // Theoretically we need to clip the damage area to the intersection of the AABB and the circle, but meh.
// TODO: Clip the area to the intersection of the box and the circle
cx, cy := (aabbMinX+aabbMaxX)/2, (aabbMinY+aabbMaxY)/2 cx, cy := (aabbMinX+aabbMaxX)/2, (aabbMinY+aabbMaxY)/2
c.Style = entityDisplayAreaDamageStyle c.Style = entityDisplayAreaDamageStyle
c.DrawPath(x+cx, y+cy, canvas.Circle(radius)) c.DrawPath(x+cx, y+cy, canvas.Circle(radius))
@ -201,7 +201,7 @@ func (e Entity) Draw(c *canvas.Context) {
c.DrawPath(x+aabbMinX, y+aabbMinY, canvas.Rectangle(aabbMaxX-aabbMinX, aabbMaxY-aabbMinY)) c.DrawPath(x+aabbMinX, y+aabbMinY, canvas.Rectangle(aabbMaxX-aabbMinX, aabbMaxY-aabbMinY))
} }
case "CollisionTriggerComponent": // Checks if another entity is inside the box with the given width and height. case "CollisionTriggerComponent": // Checks if another entity is inside the given radius and box with the given width and height.
var width, height float64 var width, height float64
path := &canvas.Path{} path := &canvas.Path{}
if member, ok := component.Members["width"]; ok { if member, ok := component.Members["width"]; ok {
@ -213,6 +213,8 @@ func (e Entity) Draw(c *canvas.Context) {
if width > 0 && height > 0 { if width > 0 && height > 0 {
path = canvas.Rectangle(width, height).Translate(-width/2, -height/2) path = canvas.Rectangle(width, height).Translate(-width/2, -height/2)
} }
// Theoretically we need to clip the area to the intersection of the box and the circle, but meh.
// TODO: Clip the area to the intersection of the box and the circle
//if member, ok := component.Members["radius"]; ok { //if member, ok := component.Members["radius"]; ok {
// if radius, ok := member.(float64); ok && radius > 0 { // if radius, ok := member.(float64); ok && radius > 0 {
// path = path.Append(canvas.Circle(radius)) // path = path.Append(canvas.Circle(radius))

View File

@ -76,7 +76,7 @@ func (mbi *MedianBlendedImage) At(x, y int) color.Color {
// Opaque returns whether the image is fully opaque. // Opaque returns whether the image is fully opaque.
// //
// For more speed and smaller filesizes, MedianBlendedImage will be marked as non-transparent. // For more speed and smaller file size, MedianBlendedImage will be marked as non-transparent.
// This will speed up image saving by 2x, as there is no need to iterate over the whole image to find a single non opaque pixel. // This will speed up image saving by 2x, as there is no need to iterate over the whole image to find a single non opaque pixel.
func (mbi *MedianBlendedImage) Opaque() bool { func (mbi *MedianBlendedImage) Opaque() bool {
return true return true

View File

@ -21,15 +21,14 @@ import (
) )
var flagInputPath = flag.String("input", filepath.Join(".", "..", "..", "output"), "The source path of the image tiles to be stitched.") var flagInputPath = flag.String("input", filepath.Join(".", "..", "..", "output"), "The source path of the image tiles to be stitched.")
var flagEntitiesInputPath = flag.String("entities", filepath.Join(".", "..", "..", "output", "entities.json"), "The source path of the entities.json file.") var flagEntitiesInputPath = flag.String("entities", filepath.Join(".", "..", "..", "output", "entities.json"), "The path to the entities.json file.")
var flagPlayerPathInputPath = flag.String("player-path", filepath.Join(".", "..", "..", "output", "player-path.json"), "The source path of the player-path.json file.") var flagPlayerPathInputPath = flag.String("player-path", filepath.Join(".", "..", "..", "output", "player-path.json"), "The path to the player-path.json file.")
var flagOutputPath = flag.String("output", filepath.Join(".", "output.png"), "The path and filename of the resulting stitched image.") var flagOutputPath = flag.String("output", filepath.Join(".", "output.png"), "The path and filename of the resulting stitched image.")
var flagScaleDivider = flag.Int("divide", 1, "A downscaling factor. 2 will produce an image with half the side lengths.") var flagScaleDivider = flag.Int("divide", 1, "A downscaling factor. 2 will produce an image with half the side lengths.")
var flagXMin = flag.Int("xmin", 0, "Left bound of the output rectangle. This coordinate is included in the output.") var flagXMin = flag.Int("xmin", 0, "Left bound of the output rectangle. This coordinate is included in the output.")
var flagYMin = flag.Int("ymin", 0, "Upper bound of the output rectangle. This coordinate is included in the output.") var flagYMin = flag.Int("ymin", 0, "Upper bound of the output rectangle. This coordinate is included in the output.")
var flagXMax = flag.Int("xmax", 0, "Right bound of the output rectangle. This coordinate is not included in the output.") var flagXMax = flag.Int("xmax", 0, "Right bound of the output rectangle. This coordinate is not included in the output.")
var flagYMax = flag.Int("ymax", 0, "Lower bound of the output rectangle. This coordinate is not included in the output.") var flagYMax = flag.Int("ymax", 0, "Lower bound of the output rectangle. This coordinate is not included in the output.")
var flagPrerender = flag.Bool("prerender", false, "Pre renders the image in RAM before saving. Can speed things up if you have enough RAM.")
var flagCleanupThreshold = flag.Float64("cleanup", 0, "Enable cleanup mode with the given threshold. This will DELETE images from the input folder, no stitching will be done in this mode. A good value to start with is 0.999, which deletes images where the sum of the min-max difference of each sub-pixel overlapping with other images is less than 99.9%% of the maximum possible sum of pixel differences.") var flagCleanupThreshold = flag.Float64("cleanup", 0, "Enable cleanup mode with the given threshold. This will DELETE images from the input folder, no stitching will be done in this mode. A good value to start with is 0.999, which deletes images where the sum of the min-max difference of each sub-pixel overlapping with other images is less than 99.9%% of the maximum possible sum of pixel differences.")
func main() { func main() {
@ -272,43 +271,30 @@ func main() {
var wg sync.WaitGroup var wg sync.WaitGroup
done := make(chan bool) done := make(chan bool)
if *flagPrerender { tempImage := NewMedianBlendedImage(tiles, outputRect)
log.Printf("Creating output image with a size of %v", outputRect.Size()) _, max := tempImage.Progress()
tempImage := image.NewRGBA(outputRect) bar.SetTotal(int64(max)).Start().SetRefreshRate(1 * time.Second)
log.Printf("Stitching %v tiles into an image at %v", len(tiles), tempImage.Bounds()) wg.Add(1)
if err := StitchGrid(tiles, tempImage, 512, bar); err != nil { go func() {
log.Panic(err) defer wg.Done()
}
bar.Finish()
outputImage = tempImage ticker := time.NewTicker(1 * time.Second)
} else { for {
tempImage := NewMedianBlendedImage(tiles, outputRect) select {
_, max := tempImage.Progress() case <-done:
bar.SetTotal(int64(max)).Start().SetRefreshRate(1 * time.Second) value, _ := tempImage.Progress()
bar.SetCurrent(int64(value))
wg.Add(1) bar.Finish()
go func() { return
defer wg.Done() case <-ticker.C:
value, _ := tempImage.Progress()
ticker := time.NewTicker(1 * time.Second) bar.SetCurrent(int64(value))
for {
select {
case <-done:
value, _ := tempImage.Progress()
bar.SetCurrent(int64(value))
bar.Finish()
return
case <-ticker.C:
value, _ := tempImage.Progress()
bar.SetCurrent(int64(value))
}
} }
}() }
}()
outputImage = tempImage outputImage = tempImage
}
log.Printf("Creating output file \"%v\"", *flagOutputPath) log.Printf("Creating output file \"%v\"", *flagOutputPath)
f, err := os.Create(*flagOutputPath) f, err := os.Create(*flagOutputPath)
@ -321,10 +307,8 @@ func main() {
log.Panic(err) log.Panic(err)
} }
if !*flagPrerender { done <- true
done <- true wg.Wait()
wg.Wait()
}
if err := f.Close(); err != nil { if err := f.Close(); err != nil {
log.Panic(err) log.Panic(err)

View File

@ -8,15 +8,10 @@ package main
import ( import (
"fmt" "fmt"
"image" "image"
"image/color"
"math" "math"
"os" "os"
"sort" "sort"
"golang.org/x/image/font"
"golang.org/x/image/font/basicfont"
"golang.org/x/image/math/fixed"
"github.com/google/hilbert" "github.com/google/hilbert"
) )
@ -103,36 +98,6 @@ func hilbertifyRectangle(rect image.Rectangle, gridSize int) ([]image.Rectangle,
return grid, nil return grid, nil
} }
func drawLabel(img *image.RGBA, x, y int, label string) {
col := color.RGBA{200, 100, 0, 255}
point := fixed.Point26_6{X: fixed.Int26_6(x * 64), Y: fixed.Int26_6(y * 64)}
d := &font.Drawer{
Dst: img,
Src: image.NewUniform(col),
Face: basicfont.Face7x13,
Dot: point,
}
d.DrawString(label)
}
func intAbs(x int) int {
if x < 0 {
return -x
}
return x
}
func pointAbs(p image.Point) image.Point {
if p.X < 0 {
p.X = -p.X
}
if p.Y < 0 {
p.Y = -p.Y
}
return p
}
// Integer division that rounds to the next integer towards negative infinity. // Integer division that rounds to the next integer towards negative infinity.
func divideFloor(a, b int) int { func divideFloor(a, b int) int {
temp := a / b temp := a / b
@ -154,10 +119,3 @@ func divideCeil(a, b int) int {
return temp return temp
} }
func maxInt(x, y int) int {
if x > y {
return x
}
return y
}