mirror of
https://github.com/Dadido3/noita-mapcap.git
synced 2024-11-18 17:17:31 +00:00
Change from blend func to interface
- Combine all previous blend modes into one blend method - Optimize BlendMethodMedian
This commit is contained in:
parent
3a73e13fb7
commit
df6c27924b
@ -11,10 +11,19 @@ import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
// BlendFuncMedian takes the given tiles and median blends them into destImage.
|
||||
func BlendFuncMedian(tiles []*ImageTile, destImage *image.RGBA) {
|
||||
// BlendMethodMedian takes the given tiles and median blends them into destImage.
|
||||
type BlendMethodMedian struct {
|
||||
LimitToNew int // If larger than 0, limits median blending to the `LimitToNew` newest tiles by file modification time.
|
||||
}
|
||||
|
||||
func (b BlendMethodMedian) Draw(tiles []*ImageTile, destImage *image.RGBA) {
|
||||
bounds := destImage.Bounds()
|
||||
|
||||
if b.LimitToNew > 0 {
|
||||
// Sort tiles by date.
|
||||
sort.Slice(tiles, func(i, j int) bool { return tiles[i].modTime.After(tiles[j].modTime) })
|
||||
}
|
||||
|
||||
// List of images corresponding with every tile.
|
||||
// Can contain empty/nil entries for images that failed to load.
|
||||
images := []*image.RGBA{}
|
||||
@ -29,7 +38,7 @@ func BlendFuncMedian(tiles []*ImageTile, destImage *image.RGBA) {
|
||||
for ix := bounds.Min.X; ix < bounds.Max.X; ix++ {
|
||||
rList, gList, bList := rListEmpty, gListEmpty, bListEmpty
|
||||
point := image.Point{ix, iy}
|
||||
found := false
|
||||
count := 0
|
||||
|
||||
// Iterate through all images and create a list of colors.
|
||||
for _, img := range images {
|
||||
@ -37,43 +46,39 @@ func BlendFuncMedian(tiles []*ImageTile, destImage *image.RGBA) {
|
||||
if point.In(img.Bounds()) {
|
||||
col := img.RGBAAt(point.X, point.Y)
|
||||
rList, gList, bList = append(rList, int(col.R)), append(gList, int(col.G)), append(bList, int(col.B))
|
||||
found = true
|
||||
count++
|
||||
// Limit number of tiles to median blend.
|
||||
// Will be ignored if LimitToNew is 0.
|
||||
if count == b.LimitToNew {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there were no images to get data from, ignore the pixel.
|
||||
if !found {
|
||||
if count == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Sort colors.
|
||||
sort.Ints(rList)
|
||||
sort.Ints(gList)
|
||||
sort.Ints(bList)
|
||||
// Sort colors. Not needed if there is only one color.
|
||||
if count > 1 {
|
||||
sort.Ints(rList)
|
||||
sort.Ints(gList)
|
||||
sort.Ints(bList)
|
||||
}
|
||||
|
||||
// Take the middle element of each color.
|
||||
var r, g, b uint8
|
||||
if l := len(rList); l%2 == 0 {
|
||||
// Even.
|
||||
r = uint8((rList[l/2-1] + rList[l/2]) / 2)
|
||||
} else {
|
||||
// Odd.
|
||||
r = uint8(rList[(l-1)/2])
|
||||
}
|
||||
if l := len(gList); l%2 == 0 {
|
||||
// Even.
|
||||
g = uint8((gList[l/2-1] + gList[l/2]) / 2)
|
||||
} else {
|
||||
// Odd.
|
||||
g = uint8(gList[(l-1)/2])
|
||||
}
|
||||
if l := len(bList); l%2 == 0 {
|
||||
// Even.
|
||||
b = uint8((bList[l/2-1] + bList[l/2]) / 2)
|
||||
} else {
|
||||
// Odd.
|
||||
b = uint8(bList[(l-1)/2])
|
||||
switch count % 2 {
|
||||
case 0: // Even.
|
||||
r = uint8((rList[count/2-1] + rList[count/2]) / 2)
|
||||
g = uint8((gList[count/2-1] + gList[count/2]) / 2)
|
||||
b = uint8((bList[count/2-1] + bList[count/2]) / 2)
|
||||
default: // Odd.
|
||||
r = uint8(rList[(count-1)/2])
|
||||
g = uint8(gList[(count-1)/2])
|
||||
b = uint8(bList[(count-1)/2])
|
||||
}
|
||||
|
||||
destImage.SetRGBA(ix, iy, color.RGBA{r, g, b, 255})
|
||||
@ -81,7 +86,7 @@ func BlendFuncMedian(tiles []*ImageTile, destImage *image.RGBA) {
|
||||
}
|
||||
}
|
||||
|
||||
// BlendNewestPixel takes the given tiles and only draws the newest pixel (based on file modification time) of any overlapping tiles.
|
||||
/*// BlendNewestPixel takes the given tiles and only draws the newest pixel (based on file modification time) of any overlapping tiles.
|
||||
func BlendNewestPixel(tiles []*ImageTile, destImage *image.RGBA) {
|
||||
bounds := destImage.Bounds()
|
||||
|
||||
@ -197,4 +202,4 @@ func BlendNewestPixelsMedian(tiles []*ImageTile, destImage *image.RGBA) {
|
||||
destImage.SetRGBA(ix, iy, color.RGBA{r, g, b, 255})
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
@ -274,7 +274,11 @@ func main() {
|
||||
var wg sync.WaitGroup
|
||||
done := make(chan struct{})
|
||||
|
||||
outputImage, err := NewStitchedImage(tiles, outputRect, BlendNewestPixelsMedian, 512, overlays)
|
||||
blendMethod := BlendMethodMedian{
|
||||
LimitToNew: 1, // Limit median blending to the n newest tiles by file modification time.
|
||||
}
|
||||
|
||||
outputImage, err := NewStitchedImage(tiles, outputRect, blendMethod, 512, overlays)
|
||||
if err != nil {
|
||||
log.Panicf("NewStitchedImage() failed: %v", err)
|
||||
}
|
||||
|
@ -17,9 +17,10 @@ import (
|
||||
// TODO: Find optimal grid size that works good for tiles with lots and few overlap
|
||||
var StitchedImageCacheGridSize = 512
|
||||
|
||||
// StitchedImageBlendFunc implements how all the tiles are blended together.
|
||||
// This is called when a new cache image needs to be generated.
|
||||
type StitchedImageBlendFunc func(tiles []*ImageTile, destImage *image.RGBA)
|
||||
// StitchedImageBlendMethod defines how tiles are blended together.
|
||||
type StitchedImageBlendMethod interface {
|
||||
Draw(tiles []*ImageTile, destImage *image.RGBA) // Draw is called when a new cache image is generated.
|
||||
}
|
||||
|
||||
type StitchedImageOverlay interface {
|
||||
Draw(*image.RGBA)
|
||||
@ -28,10 +29,10 @@ type StitchedImageOverlay interface {
|
||||
// StitchedImage combines several ImageTile objects into a single RGBA image.
|
||||
// The way the images are combined/blended is defined by the blendFunc.
|
||||
type StitchedImage struct {
|
||||
tiles []ImageTile
|
||||
bounds image.Rectangle
|
||||
blendFunc StitchedImageBlendFunc
|
||||
overlays []StitchedImageOverlay
|
||||
tiles []ImageTile
|
||||
bounds image.Rectangle
|
||||
blendMethod StitchedImageBlendMethod
|
||||
overlays []StitchedImageOverlay
|
||||
|
||||
cacheHeight int
|
||||
cacheImage *image.RGBA
|
||||
@ -40,12 +41,12 @@ type StitchedImage struct {
|
||||
}
|
||||
|
||||
// NewStitchedImage creates a new image from several single image tiles.
|
||||
func NewStitchedImage(tiles []ImageTile, bounds image.Rectangle, blendFunc StitchedImageBlendFunc, cacheHeight int, overlays []StitchedImageOverlay) (*StitchedImage, error) {
|
||||
func NewStitchedImage(tiles []ImageTile, bounds image.Rectangle, blendMethod StitchedImageBlendMethod, cacheHeight int, overlays []StitchedImageOverlay) (*StitchedImage, error) {
|
||||
if bounds.Empty() {
|
||||
return nil, fmt.Errorf("given boundaries are empty")
|
||||
}
|
||||
if blendFunc == nil {
|
||||
return nil, fmt.Errorf("no blending function given")
|
||||
if blendMethod == nil {
|
||||
return nil, fmt.Errorf("no blending method given")
|
||||
}
|
||||
if cacheHeight <= 0 {
|
||||
return nil, fmt.Errorf("invalid cache height of %d pixels", cacheHeight)
|
||||
@ -54,7 +55,7 @@ func NewStitchedImage(tiles []ImageTile, bounds image.Rectangle, blendFunc Stitc
|
||||
return &StitchedImage{
|
||||
tiles: tiles,
|
||||
bounds: bounds,
|
||||
blendFunc: blendFunc,
|
||||
blendMethod: blendMethod,
|
||||
overlays: overlays,
|
||||
cacheHeight: cacheHeight,
|
||||
cacheImage: &image.RGBA{},
|
||||
@ -148,7 +149,7 @@ func (si *StitchedImage) regenerateCache(rect image.Rectangle) {
|
||||
}
|
||||
|
||||
// Blend tiles into image at the workload rectangle.
|
||||
si.blendFunc(workloadTiles, cacheImage.SubImage(workload).(*image.RGBA))
|
||||
si.blendMethod.Draw(workloadTiles, cacheImage.SubImage(workload).(*image.RGBA))
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user