mirror of
				https://github.com/Dadido3/noita-mapcap.git
				synced 2025-10-31 03:09:35 +00:00 
			
		
		
		
	- Add compatibility for newest Noita beta - Modify STREAMING_CHUNK_TARGET, GRID_MAX_UPDATES_PER_FRAME and GRID_MIN_UPDATES_PER_FRAME magic numbers for a more robust capturing process - Add LimitGroup to util.go - Add webp-level command line flag to define the webp compression level - Rework progress bar to make it work in DZI export mode - Refactor image exporter functions - Use LimitGroup to make DZI export multithreaded - Add BlendMethodFast which doesn't mix tile pixels - Up Go version to 1.22 - Use Dadido3/go-libwebp for WebP encoding
		
			
				
	
	
		
			141 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			141 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) 2019-2024 David Vogel
 | |
| //
 | |
| // This software is released under the MIT License.
 | |
| // https://opensource.org/licenses/MIT
 | |
| 
 | |
| package main
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"image"
 | |
| 	"os"
 | |
| 	"sync"
 | |
| )
 | |
| 
 | |
| // QuickSelect returns the kth smallest element of the given unsorted list.
 | |
| // This is faster than sorting the list and then selecting the wanted element.
 | |
| //
 | |
| // Source: https://rosettacode.org/wiki/Quickselect_algorithm#Go
 | |
| func QuickSelectUInt8(list []uint8, k int) uint8 {
 | |
| 	for {
 | |
| 		// Partition.
 | |
| 		px := len(list) / 2
 | |
| 		pv := list[px]
 | |
| 		last := len(list) - 1
 | |
| 		list[px], list[last] = list[last], list[px]
 | |
| 
 | |
| 		i := 0
 | |
| 		for j := 0; j < last; j++ {
 | |
| 			if list[j] < pv {
 | |
| 				list[i], list[j] = list[j], list[i]
 | |
| 				i++
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// Select.
 | |
| 		if i == k {
 | |
| 			return pv
 | |
| 		}
 | |
| 		if k < i {
 | |
| 			list = list[:i]
 | |
| 		} else {
 | |
| 			list[i], list[last] = list[last], list[i]
 | |
| 			list = list[i+1:]
 | |
| 			k -= i + 1
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Source: https://gist.github.com/sergiotapia/7882944
 | |
| func GetImageFileDimension(imagePath string) (int, int, error) {
 | |
| 	file, err := os.Open(imagePath)
 | |
| 	if err != nil {
 | |
| 		return 0, 0, fmt.Errorf("can't open file %v: %w", imagePath, err)
 | |
| 	}
 | |
| 	defer file.Close()
 | |
| 
 | |
| 	image, _, err := image.DecodeConfig(file)
 | |
| 	if err != nil {
 | |
| 		return 0, 0, fmt.Errorf("error decoding config of image file %v: %w", imagePath, err)
 | |
| 	}
 | |
| 
 | |
| 	return image.Width, image.Height, nil
 | |
| }
 | |
| 
 | |
| func GridifyRectangle(rect image.Rectangle, gridSize int) (result []image.Rectangle) {
 | |
| 	for y := DivideFloor(rect.Min.Y, gridSize); y <= DivideCeil(rect.Max.Y-1, gridSize); y++ {
 | |
| 		for x := DivideFloor(rect.Min.X, gridSize); x <= DivideCeil(rect.Max.X-1, gridSize); x++ {
 | |
| 			tempRect := image.Rect(x*gridSize, y*gridSize, (x+1)*gridSize, (y+1)*gridSize)
 | |
| 			intersection := tempRect.Intersect(rect)
 | |
| 			if !intersection.Empty() {
 | |
| 				result = append(result, intersection)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // Integer division that rounds to the next integer towards negative infinity.
 | |
| func DivideFloor(a, b int) int {
 | |
| 	temp := a / b
 | |
| 
 | |
| 	if ((a ^ b) < 0) && (a%b != 0) {
 | |
| 		return temp - 1
 | |
| 	}
 | |
| 
 | |
| 	return temp
 | |
| }
 | |
| 
 | |
| // Integer division that rounds to the next integer towards positive infinity.
 | |
| func DivideCeil(a, b int) int {
 | |
| 	temp := a / b
 | |
| 
 | |
| 	if ((a ^ b) >= 0) && (a%b != 0) {
 | |
| 		return temp + 1
 | |
| 	}
 | |
| 
 | |
| 	return temp
 | |
| }
 | |
| 
 | |
| // https://gist.github.com/cstockton/d611ced26bb6b4d3f7d4237abb8613c4
 | |
| type LimitGroup struct {
 | |
| 	wg   sync.WaitGroup
 | |
| 	mu   *sync.Mutex
 | |
| 	c    *sync.Cond
 | |
| 	l, n int
 | |
| }
 | |
| 
 | |
| func NewLimitGroup(n int) *LimitGroup {
 | |
| 	mu := new(sync.Mutex)
 | |
| 	return &LimitGroup{
 | |
| 		mu: mu,
 | |
| 		c:  sync.NewCond(mu),
 | |
| 		l:  n,
 | |
| 		n:  n,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (lg *LimitGroup) Add(delta int) {
 | |
| 	lg.mu.Lock()
 | |
| 	defer lg.mu.Unlock()
 | |
| 	if delta > lg.l {
 | |
| 		panic(`LimitGroup: delta must not exceed limit`)
 | |
| 	}
 | |
| 	for lg.n < 1 {
 | |
| 		lg.c.Wait()
 | |
| 	}
 | |
| 	lg.n -= delta
 | |
| 	lg.wg.Add(delta)
 | |
| }
 | |
| 
 | |
| func (lg *LimitGroup) Done() {
 | |
| 	lg.mu.Lock()
 | |
| 	defer lg.mu.Unlock()
 | |
| 	lg.n++
 | |
| 	lg.c.Signal()
 | |
| 	lg.wg.Done()
 | |
| }
 | |
| 
 | |
| func (lg *LimitGroup) Wait() { lg.wg.Wait() }
 |