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)) }