noita-mapcap/bin/stitch/imagetile.go

114 lines
2.6 KiB
Go
Raw Permalink Normal View History

2020-05-30 16:16:50 +00:00
// Copyright (c) 2019-2020 David Vogel
2019-10-21 00:07:39 +00:00
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
package main
import (
"fmt"
"image"
_ "image/png"
"os"
"sync"
"time"
"github.com/nfnt/resize"
2019-10-21 00:07:39 +00:00
)
type imageTile struct {
fileName string
scaleDivider int // Downscales the coordinates and images on the fly.
offset image.Point // Correction offset of the image, so that it aligns pixel perfect with other images. Determined by image matching.
image image.Image // Either a rectangle or an RGBA image. The bounds of this image are determined by the filename.
imageMutex *sync.RWMutex //
imageUsedFlag bool // Flag signalling, that the image was used recently
2019-11-30 17:28:17 +00:00
pixelErrorSum uint64 // Sum of the difference between the (sub)pixels of all overlapping images. 0 Means that all overlapping images are identical.
2019-10-21 00:07:39 +00:00
}
func (it *imageTile) GetImage() (*image.RGBA, error) {
it.imageMutex.RLock()
it.imageUsedFlag = true // Race condition may happen on this flag, but doesn't matter here.
2019-10-21 00:07:39 +00:00
// Check if the image is already loaded
if img, ok := it.image.(*image.RGBA); ok {
it.imageMutex.RUnlock()
return img, nil
}
it.imageMutex.RUnlock()
// It's possible that the image got changed in between here
it.imageMutex.Lock()
defer it.imageMutex.Unlock()
// Check again if the image is already loaded
if img, ok := it.image.(*image.RGBA); ok {
return img, nil
2019-10-21 00:07:39 +00:00
}
// Store rectangle of the old image
oldRect := it.image.Bounds()
file, err := os.Open(it.fileName)
if err != nil {
return &image.RGBA{}, err
2019-10-21 00:07:39 +00:00
}
defer file.Close()
img, _, err := image.Decode(file)
if err != nil {
return &image.RGBA{}, err
2019-10-21 00:07:39 +00:00
}
if it.scaleDivider > 1 {
img = resize.Resize(uint(oldRect.Dx()), uint(oldRect.Dy()), img, resize.NearestNeighbor)
}
2019-10-21 00:07:39 +00:00
imgRGBA, ok := img.(*image.RGBA)
if !ok {
return &image.RGBA{}, fmt.Errorf("Expected an RGBA image, got %T instead", img)
2019-10-21 00:07:39 +00:00
}
// Restore the position of the image rectangle
imgRGBA.Rect = imgRGBA.Rect.Add(oldRect.Min)
it.image = imgRGBA
// Free the image after some time
go func() {
for it.imageUsedFlag {
time.Sleep(1 * time.Second)
it.imageUsedFlag = false
}
it.imageMutex.Lock()
defer it.imageMutex.Unlock()
it.image = it.image.Bounds()
}()
return imgRGBA, nil
2019-10-21 00:07:39 +00:00
}
func (it *imageTile) OffsetBounds() image.Rectangle {
it.imageMutex.RLock()
defer it.imageMutex.RUnlock()
return it.image.Bounds().Add(it.offset)
}
func (it *imageTile) Bounds() image.Rectangle {
it.imageMutex.RLock()
defer it.imageMutex.RUnlock()
return it.image.Bounds()
2019-10-21 00:07:39 +00:00
}
func (it *imageTile) String() string {
return fmt.Sprintf("<ImageTile \"%v\">", it.fileName)
}