Files
imageutils/cmd/downscale2x/downscale.go
2025-12-14 06:04:57 +03:00

59 lines
1.3 KiB
Go

package main
import (
"errors"
"image"
"image/color"
)
var errOutOfBounds error = errors.New("downscale: out of bounds")
type Downscaled struct {
image.Image
Factor int
}
func isOutOfBounds(pt image.Point, img image.Image) bool {
x, y := img.Bounds().Dx(), img.Bounds().Dy()
if pt.X > x || pt.Y > y {
return true
}
return false
}
func meanColor(palette color.Palette) color.Color {
var r, g, b int
for _, v := range palette {
c := color.RGBAModel.Convert(v).(color.RGBA)
r += int(c.R)
g += int(c.G)
b += int(c.B)
}
r, g, b = r/len(palette), g/len(palette), b/len(palette)
return color.RGBA{uint8(r), uint8(g), uint8(b), 0xff}
}
func (d Downscaled) ColorModel() color.Model { return d.Image.ColorModel() }
func (d Downscaled) Bounds() image.Rectangle {
return image.Rect(0, 0, d.Image.Bounds().Dx()/d.Factor, d.Image.Bounds().Dy()/d.Factor)
}
func (d Downscaled) At(x, y int) color.Color {
palette := color.Palette{}
pt := image.Point{x * d.Factor, y * d.Factor}
if isOutOfBounds(pt, d.Image) {
panic(errOutOfBounds)
}
for i := 0; i < d.Factor; i++ {
for j := 0; j < d.Factor; j++ {
currentPt := image.Point{pt.X + i, pt.Y + j}
if i > 0 || j > 0 {
if isOutOfBounds(currentPt, d.Image) {
continue
}
}
palette = append(palette, d.Image.At(currentPt.X, currentPt.Y))
}
}
return palette.Convert(meanColor(palette))
}